C++ 中的常量表达式和编译器优化

C++干货系列——从编译期常量谈到constexpr(一) 从编译期常量谈起 C++20都支持虚函数的constexpr了,我打算用三篇读文章讲清楚编译期常量和constexpr这个东西和编译期常量的关系,即为什么需要他来辅助解决这个问题。最后帮助读者在实际编码过程中能够有意识地去运用他们,这才是终极目标。这篇文章中会讲到隐藏在日常编程中的各种编译期常量,以及他们存在的意义。 什么是编译期常量 想要用编译期常量就要首先知道它们是什么,一般出现在哪里和运行期常量有什么区别,因此我打算用第一篇文章重点分析编译期常量以及使用他们有什么好处。 编译期常量(Compile-t 阅读详情

本文有助于理解哪些是编译期执行,哪些是运行期执行,也有助于理解模板

as-if规则

在C++中,编译器被赋予了很大的空间去优化程序。as-if规则说的就是:对编译器来说,只要不改变程序的可观测的行为,编译器可以对程序作任何旨在产生更优化代码的任何改动。

对这个规则有一个例外:不必要的对拷贝构造函数的调用可被优化掉,即便这些拷贝构造函数能够产生可被观测的行为。

虽然编译器如何对一个程序进行优化取决于编译器本身,但是我们可以作一些工作来使得编译器优化的更好。

优化时机

考虑下述代码

#include <iostream>

int main()
{
	int x { 3 + 4 };
	std::cout << x << '\n';

	return 0;
}

我们很容易知道,输出为7.

但是,背后有一个很有趣的优化方案。

如果这个程序就像所写的代码那样被编译(没有任何优化),编译器就会产生计算3+4的二进制代码,用于在运行期使用。要是这些代码执行一百万次,3+4就会被计算一百万次,7这个结果就会被产生一百万次。由于3+4的计算结果7永远不会改变,一遍又一遍的重复计算就非常浪费时间和资源。

编译期表达式求值

现代C++编译器能够在编译期对一些表达式进行求值。在这种情形下,编译器就用求值后的值来代替表达式。例如,编译器可以对上面的代码进行如此优化:

#include <iostream>

int main()
{
	int x { 7 };
	std::cout << x << '\n';

	return 0;
}

这段代码与上述代码一样输出7的结果,但是优化后的执行代码不再需要在运行时浪费CPU周期来计算3+4。而且,我们不需要作任何额外的事情来获得这项好处(当然你的编译器优化开关需要打开)。

好处:

编译期求值允许编译器在编译期做一些工作,否则的话这些工作就要在运行期完成。这些优化可能会使我们的编译过程更长,但是由于表达式只需在编译期求值一次,免除了在运行期的计算,这会让我们的执行代码运行更快和使用更少的内存。

常量表达式

有一种总是在编译期就可以被求值的表达式叫做“常量表达式”。常量表达式的准确定义非常复杂,我们这里采用一种简化的方法:常量表达式就是这么一种表达式:它仅仅包含编译期常量和支持编译期求值的操作符或者函数。下面的表述中,常量表达式和运行期常量是一个意思:

编译期常量就是这么一个常量:其值在编译期就已经知道。包括:

  • 字面量(比如'5','1.2')
  • 有Constexpr修饰的变量
  • 带有一个常量表达式初始化的const型的整型变量(比如: const int x{5}),在现代C++中,更倾向于使用constexpr
  • 非类型的模板形参(整形值作为模板形参)
  • 枚举

其他的不是编译期常量的常量变量也叫运行期常量,运行期常量不能用在常量表达式中。

一个非整型的常量变量(Const non-integral variables)总是运行期常量(即便他们有一个常量表达式初始化)。如果你需要这样的变量成为编译期常量,把他们定义为constexpr变量。

最重用的支持编译期求值的操作符和函数包括:

  • 使用编译期常量作操作数的算术运算符(比如1+2)
  • constexpt和consteval函数。

下面的例子中,我们识别一些常量表达式和非常量表达式,我们也识别哪些变量是非常量,运行时常量,或者编译期常量。

#include <iostream>

int getNumber()
{
    std::cout << "Enter a number: ";
    int y{};
    std::cin >> y;

    return y;
}

int main()
{
    // 非常量变量:
    int a { 5 };                 // 5 是常量表达式
    double b { 1.2 + 3.4 };      // 1.2 + 3.4 是常量表达式

    // 带有一个常量初始化的常整型变量是编译期常量
    const int c { 5 };           // 5 是常量表达式
    const int d { c };           // c 是常量表达式
    const long e { c + 2 };      // c + 2 是常量表达式

    // 其他的常型变量是运行时常量:
    const int f { a };           // a 不是常量表达式
    const int g { a + 1 };       // a + 1 不是常量表达式
    const long h { a + c };      // a + c 不是常量表达式
    const int i { getNumber() }; // getNumber() 不是常量表达式

    const double j { b };        // b 不是常量表达式
    const double k { 1.2 };      // 1.2 是常量表达式

    return 0;
}

不是常量表达式的表达式又叫运行期表达式。例如:std::cout << x << '\n' 是一个运行时表达式,这是因为x不是一个编译期常量,operator<<也不支持编译期求值(因为输出操作在编译期无法执行)。

我们为什么关注常量表达式

至少有三个原因使得常量表达式非常有用:

  • 常量表达式总是可以在编译期被求值,也意味着他们可以在编译期被优化。这会产生更快和更小的代码。
  • 对运行期表达式,在编译期期间,只能知道它的类型。对常量表达式,表达式的类型和值在编译期期间都能确定。这会允许我们在编译期对其值进行合法性检查。如果这个值不满足我们的需求,我们可以输出编译错误,这就允许我们立即识别和更正发现的问题。这样的结果就是代码更安全,更容易测试,更不容易误用。
  • 有些C++特性需要常量表达式(下面描述)

常量表达式通常需要满足以下条件:

  • constexpr变量需要一个初始化
  • 非类型模板行参(值行参模板)
  • 有确定长度的std::array

常量表达式包括constexpr变量和constexpr函数。

常量表达式在什么时刻被求值?

编译器仅在编译期上下文要求对一个常量表达式进行求值的时候才求值(比如编译期常量初始化),在上下文不需要常量表达式的地方,编译器可能在编译期或者运行时进行求值。

const int x { 3 + 4 }; // 常量表达式 3 + 4 必须在编译期进行求值
int y { 3 + 4 };       // 常量表达式3 + 4 可能在编译期也可能在运行期进行求值

这是因为变量x的类型为const int,并且有一个常量表达式初始化,所以它是一个编译期常量。它的初始化必须在编译期进行求值(除非x的值在编译期不知道,或者x不是一个编译期常量)

对于变量y,它不需要一个常量表达式初始化(没有const),所以编译器可以在编译期或者运行期对其进行求值。

即便不需要这么做,现代编译器通常对常量表达式在编译期进行求值,这是因为这么做更容易优化,效率更高。

子常量表达式的部分优化
#include <iostream>

int main()
{
	std::cout << 3 + 4 << '\n';

	return 0;
}

全表达式std::cout << 3 + 4 << '\n';是一个运行时表达式,这是因为它的输出只能在运行时执行。但是我们注意到全部表达式里面包含一个子表达式3+4.

编译器一直以来就可以对一个常量子表达式进行优化,即便这个全表达式是一个运行期表达式。于是这个全表达式优化以后的结果就是:

#include <iostream>

int main()
{
	std::cout << 7 << '\n';

	return 0;
}
非常量表达式的优化

编译器甚至可以对一些非常量表达式或者子表达式进行优化,让我们看前面的例子:

#include <iostream>

int main()
{
	int x { 7 };            // x is 非常量表达式
	std::cout << x << '\n'; // x 一个非常量子表达式

	return 0;
}

当x被初始化的时候,值7就被存储在为x分配的内存中。于是下一行代码就会直接到内存中获取7这个值。

即便x是一个非常量表达式,聪明的编译器能够意识到x在这段代码中永远被求值为7,因此在as-if规则下,可以优化这段代码为:

#include <iostream>

int main()
{
	int x { 7 };
	std::cout << 7 << '\n';

	return 0;
}

由于x在程序中不再被使用到,因此编译器甚至进一步将程序优化为:

#include <iostream>

int main()
{
	std::cout << 7 << '\n';

	return 0;
}

在这个版本中,变量x已经彻底被移除掉了(因为它不再被使用,因此不再需要了)。当一个变量被从一个程序中移除,我们称这个变量被优化掉或者优化删除。

但是,由于x不是一个常量,这样的优化需要编译器能识别出x的值在整个程序中确实没有变化。编译器是否能识别出这一点,归结到这个程序的复杂度或者编译器优化功能的复杂度。

常变量很容易被优化

看下面的例子

#include <iostream>

int main()
{
	const int x { 7 }; // x is 常变量
	std::cout << x << '\n';

	return 0;
}

在这个版本中,编译器不再需要推断x在整个程序中不会变化,因为x是一个const变量,这就保证了这个变量在初始化之后,就不再变了。这就使得编译器很容易得出结论这个变量可以被很安全地从代码中移除。

能被编译器优化的各种变量的排序如下:

  • 编译期常变量(总可以被优化)
  • 运行期常变量
  • 非常变量(Non-const variables)(简单情况下会被优化)

把一个变量常量化有助于编译器进行编译优化

编译期常变量可被用在常量表达式中,比运行期表达式更大可能在编译期被求值。

编译优化使得程序调试更难

当编译器对程序进行优化时,变量,表达式,语句和函数调用可能被重新安排顺序,改变,用值替代,甚至彻底移除,这些变化会影响调试。

在编译期,我们很难直观或者有工具帮助我们理解编译器作了什么。如果一个变量或者表达式被一个值取代了,假设那个值是一个错误的值,我们怎么调试?

为了减少这些问题,我们一般在调试的时候关闭编译优化,于是编译后的代码和我们的原始代码就非常接近。

理解 C 与 C++ 中的 const 常量与数组大小的关系 在 C 语言和 C++ 中,数组的大小通常要求是一个编译时常量,也就是说数组的长度必须在编译阶段就能够确定。这对于程序的性能优化和内存管理是至关重要的。在很多初学者学习这两门语言时,对于数组大小的常量有时会感到困惑,尤其是在遇到 const 关键字时。在此篇文章中,我们将详细探讨 C 和 C++ 中数组大小的常量要求,分析 const 在这两种语言中的作用,解答为什么在 C 中常量 const int a = 10; 无法作为数组大小 2025.2.5-✍60min 阅读详情

相关推荐

C++中的constexpr与常量表达式

在C++中,constexpr是一个关键字,用于声明常量表达式。在C++11标准中,constexpr只能用于声明常量值,而在C++14标准中,它的用法得到了扩展,可以用于声明函数、构造函数和类成员函数。需要注意的是,constexpr变量和函数在使用时有一些限制条件,比如函数体内不能包含非常量操作(C++17标准中有一些例外),并且constexpr函数的参数和返回值类型必须是字面类型。首先,我们可以使用constexpr关键字声明一个常量变量,该变量的值必须在编译时就能够确定,并且不能被修改。

PixelBlade的博客 312

C++ 学习系列 -- 运行时常量 const 与 编译期常量 constexpr 介绍

C++ 中引入了 const 关键字,被 const 关键字修饰的变量其值不能修改,是只读的。

qq_33775774的博客 640

ESP32锂电池电量检测实战:从引脚选择到低功耗优化(附完整电路图)

本文详细介绍了ESP32锂电池电量检测的实战方案,从ADC引脚选择到低功耗优化,提供完整的电路图和代码实现。通过MOS管智能开关和深度睡眠技术,显著降低静态功耗至0.5μA,提升设备续航能力。适合物联网开发者解决电量检测中的常见问题。

weixin_28731223的博客 461

C++编译期间类中的常量

<br />  问题:在类中建立一个int类型的数组   方法一:(错误)   class Test   {   const int size = 100;   int array[size];   //……   };   错误原因:   1:因为在一个类中,const恢复了它在c中的一部分意思,在每个类对象里分配存储并代表一个值,这个值一旦被初始化以后就不能被改变。所以在类中使用了const的意思是:在这个对象的生命周期内,它是一个常量。   然而,每个对象可能包含不同的值。   2:对const常量进行

batoom的专栏 1314

Diffix3D+复现

上下载模型,将其放在一个目录中,并将目录名填在demo代码中from_pretrained的cache_dir参数里。目录结构应该是"<PATH>/nvidia/difix/…给的requirements文件中pytorch和xformers的版本没有指定,这里测试过以下版本,可以成功运行。将readme中的demo代码新建一个python脚本复制进去,然后将脚本放在src目录中运行。如果遇到网络问题无法加载huggingface上的预训练模型,可以手动从。如果出现,以下错误重新。

NeverMakeIt的博客 1085

避开这些坑!DS18B20温度读取不准、时序混乱的5个常见问题与调试方法

本文深入解析DS18B20温度传感器在应用中常见的5个问题,包括温度读取不准、时序混乱等,并提供详细的调试方法。通过硬件设计优化、精准延时实现和数据解析技巧,帮助开发者有效解决温度值跳变、初始化失败等典型问题,提升OneWire协议下的温度测量精度。

weixin_29185493的博客 463

C++11:常量表达式

常量表达式主要是允许一些计算发生在编译时,即发生在代码编译阶段而不是代码运行阶段。这是很大的优化,因为如果有些事情可以在编译时做,那么它只会做一次,而不是每次程序运行时都计算。 使用constexpr,你可以创建一个编译时的函数: constexpr int getConst() { return 3; } void test07() { int arr[getConst()] = { 0 }...

weixin_43718250的博客 957

李航《统计学习方法》课后习题答案(第2版)

李航《统计学习方法》课后习题答案(第2版) 章节 链接 第1章 统计学习及监督学习概论 点击进入 第2章 感知机 点击进入 第3章 k近邻法 点击进入 第4章 朴素贝叶斯法 点击进入 第5章 决策树 点击进入 第6章 逻辑斯谛回归与最大熵 点击进入 第7章 支持向量机 点击进入 第8章 提升方法 点击进入 第9章 EM算法及其推广 点击进入 第10章 隐马尔可夫模型 点击进入 第11章 条件随机场 点击进入     也可进入我的个人主页查看分类专栏

weixin_43646592的博客 2万+

Java 编译期常量

Java 编译期常量 1. 什么是编译期常量  编译期常量,即 compile-time constant。其看似是一个静态,并不一定是由 static 修饰(static 一般只是用于强调只有一份),但强制要求使用 final 进行修饰。编译期常量完整要求是: declared final;被声明为 final(所有编译期常量都满足的条件); primitive or String;基本类型或...

li_xunhuan的博客 1366

Oracle11g下载与安装(windows)

一、Oracle11g下载 二、安装准备 三、执行安装步骤 四、测试连接 附:Oracle11g服务说明

欢迎订阅我的“JAVA全栈开发笔记(全)”专栏,价格优惠,仅需19.9元。 2万+

YOLOv11改进策略【Head】| AFPN渐进式自适应特征金字塔,增加针对小目标的检测头(附模块详解和完整配置步骤)

一、本文介绍 本文利用AFPN对YOLOv11的网络模型进行优化提升。AFPN采用渐近式融合及自适应空间融合操作。通过渐近式架构,逐步整合不同层级特征,有效避免非相邻层级间因语义差距过大导致的信息丢失或降级问题,确保在多尺度特征融合过程中既能保留高层语义信息,又能融入低层细节特征。本文将AFPN应用于YOLOv11中,并配置了针对小目标的检测头,能显著增强模型对不同尺度目标的检测能力,使其在复杂场景下更聚焦于目标物体特征,减少背景等无关信息的影响。 专栏目录:YOLOv11改进目录一览 | 涉及卷积层、

Limiiiing的博客 2718

6.4内联函数和constexpr函数

内联函数和constexpr函数 我们先看一个函数,这个函数的功能是比较两个string形参的长度并返回较小的string的引用。 string &shorterString(const string &s1, const string &s2) { return s1.size() > s2.size() ? s2 : s1; } 这种规模比较小的操作定义成函数有很多好处,包括: 阅读shorterString函数比阅读等价表达式容易得多 增加通用性,每次相关操作

weixin_44897707的博客 244

2022年全国职业院校技能大赛 高职组大数据技术与应用卷⑦——解析答案

大数据时代,电商经营模式发生很大改变。在传统运营模式中,缺乏数据积累,人们在做出一些决策行为过程中,更多是凭借个人经验和直觉,发展路径比较自我封闭。而大数据时代,为人们提供一种全新的思路,通过大量的数据分析得出的结果将更加现实和准确。商家可以对客户的消费行为信息数据进行收集和整理,比如消费者购买产品的花费、选择产品的渠道、偏好产品的类型、产品回购周期、购买产品的目的、消费者家庭背景、工作和生活环境、个人消费观和价值观等。通过数据追踪,知道顾客从哪儿来,是看了某网站投放的广告还是通过朋友推荐链接,是新访客还是

我是网络安全兼技能大赛工程师,专注网络安全技术学习与技能大赛实战!持续分享最新的技术文章,帮你少走弯路,一起在技术赛道上进步~ 471

C++ 中的常量表达式

常量表达式是 C++11 引入的一种机制,旨在允许编译器在编译阶段就计算出某些表达式的值,而不是在运行时计算。这使得程序能够更加高效,因为常量表达式的值可以在编译时就确定下来,从而避免了运行时的计算开销。变量的值必须在编译时已知,并且通常要求是编译时常量表达式(即,能通过常量计算得到的值)。判断一个表达式是否为常量表达式,关键是看该表达式的值是否能在编译时确定。函数或变量的值,或者依赖于运行时输入的表达式,不能成为常量表达式。声明的变量在编译时可以确定,但它的值不一定能在编译时求出。

weixin_73931631的博客 525

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

C++11——constexpr常量表达式及演进

上半部分解释了什么是常量表达式,下半部分解释了constexpr关键字如何使用,简单来说就是,没有指针的情况下,使用constexpr表示常量表达式(编译器会优化),有*下,使用关键字修饰的是=左边,即代表=左边的变量已经定死了,这就是为什么p1会报错,因为p1是指针的指向内容无法修改,但指向可以修改。另外constexpr成员函数不能是虚函数。通俗地讲,const修饰的内容,以及指针指向的内容无法修改,但指针指向可以修改的是底层const,无法修改指针指向,但指针指向的内容可以修改的是顶层const。

czt230610的博客 684

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

常量和常量表达式3. C&C++中的区别

对于代码const int a = 5;int b = a + 10; C语言相应的汇编指令为`mov eax, [rbp-4]`(读a的内存)→ `add eax, 10` → `mov [rbp-8], eax`(赋值给b);而C++的汇编指令为mov dword ptr [rbp-8], 0xF`(直接赋值15,无内存读取);

rainsssssss的博客 383

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

C++:常量表达式

简单总结了C++中的常量表达式,着重总结了constexpr的使用

LZ的博客 849

C++ 之constexpr、常量表达式与字面值类型关系以及使用常量表达式的原因

constexpr与常量表达式、字面值类型、字面值常量类、枚举enum 目录 constexpr与常量表达式 字面值类型 字面值常量类 枚举enum constexpr与常量表达式 constexpr类型——constexpr变量 含义:声明为constexpr类型的变量,一定是常量,一定要用常量表达式初始化(编译器验证)。 PS: 不能用普通函数初始化,但可以用constexptr函数初始化 constexpr类型constexptr函数(内联函数和const...

qq_31946663的博客 1259

const和constexpr常量表达式

值不会发生改变编译阶段就能计算出确定结果简单来说:编译器在翻译代码时,就能 100% 确定它的最终值,不需要等待程序运行时计算。常量表达式是属性:描述一个值能否在编译期确定const是语法约束:表示「运行时不可修改」,一部分const变量具备常量表达式属性constexpr是强制标记:强制变量必须是常量表达式,由编译器做编译期检查理解了常量表达式的本质,你就能避开 C++ 里绝大多数和 const、编译期常量相关的坑,写出更安全、性能更好的代码。

2501_94496474的博客 357

什么是 C++ 中的常量表达式? 有什么用途?如何判断一个表达式是否是常量表达式?

,`c`是常量表达式,`2`是常量表达式,它们通过加法运算组合后的`d`也是常量表达式。- 如果一个函数或者变量被声明为`constexpr`,并且满足`constexpr`的要求(如`constexpr`函数的函数体只能包含返回语句等简单操作,`constexpr`变量必须用常量表达式初始化),那么它就是常量表达式。- 当一个表达式是由多个子表达式通过算术(`+`、`-`、`*`、`/`等)、逻辑(`&&`、`||`、`!`等)、位运算(`&`、`|`、`^`等)组合而成时,需要检查每个子表达式。

2202_75941514的博客 1366

什么是 C++ 中的常量表达式? 有什么用途?and如何判断一个表达式是否是常量表达式?

在 C++ 中,常量表达式是一种在编译期间就能计算出结果的表达式。字面量常量:如整数字面量(1、2、3等)、字符字面量('a'、'b'等)、布尔字面量(true、false)和浮点字面量(3.14、2.718等)。例如,表达式3 + 4中的3和4就是整数字面量,整个表达式在编译时可以计算出结果为7。const修饰的变量(满足一定条件):当一个变量被const修饰并且它的初始值是一个常量表达式时,这个变量也可以用于常量表达式。例如,const int a = 5;

m0_73949707的博客 1093

c++ constexpr和常量表达式

constexpr & 常量表达式

Here_JW的博客 582

constexpr和常量表达式

一、含义1、常量表达式:指值不会改变并且在编译过程就能得到计算结果的表达式。 如:const int max_files = 20; //max_files是常量表达式 const int limit = max_files + 1; //limit是常量表达式 int staff_size = 27; //staff_size不是常量

做一个匠人 4174

C/C++编译错误E0028解析:常量表达式与数组大小定义

在C/C++编程中,常量表达式是一个核心的编译期概念,它要求表达式的值在编译阶段就必须完全确定。这一特性源于程序内存模型对栈空间静态分配的需求,编译器需要提前知道数据的确切大小以规划内存布局。理解常量表达式的原理,对于编写高性能、可预测的底层代码至关重要,尤其在嵌入式系统、高性能计算和系统编程等场景中。本文围绕常见的E0028编译错误,深入探讨了常量表达式的定义、C与C++标准的差异,以及如何通过宏定义、const变量、constexpr关键字和动态内存分配等技术手段解决数组大小定义问题。通过分析编译期与运

weixin_34068198的博客 373

C++11 constexpr:验证是否为常量表达式及与const的区别

constexpr 是 C++ 11 标准新引入的关键字,不过在讲解其具体用法和功能之前,读者需要先搞清楚 C++ 常量表达式的含义。 所谓常量表达式,指的就是由多个(≥1)常量组成的表达式。换句话说,如果表达式中的成员都是常量,那么该表达式就是一个常量表达式。这也意味着,常量表达式一旦确定,其值将无法修改。 实际开发中,我们经常会用到常量表达式。以定义数组为例,数组的长度就必须是一个常量表达式: // 1) int url[10];//正确 // 2) int url[6 + 4];//正确 // 3)

Hardy20200507的博客 274
上一篇: 理解参数依赖定位(ADL)规则
下一篇: 理解C++中的右值引用
SONG_CA
博客等级 码龄24年 74粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值