宏定义中的特殊参数(#、##、...和__VA_ARGS__)

iOS 开发 高级:使用 宏定义macros (#,##,...,__VA_ARGS_) 一直以来用宏定义#define也就是定义一些简单的常量,至多也就是定义一个函数,很少关注宏定义的用法。直到看到这样的代码: #define PLAYSOUNDEFFECT(...) \ [[GameManager sharedGameManager] playSoundEffect:@#__VA_ARGS__] 这么强大的用法以前从来没有想过。看一下iOS Framework的 阅读详情




参考:http://blog.youkuaiyun.com/cqupt_chen/article/details/8055215





最近在android的某个代码的头文件中发现很多__VA_ARGS__,google一下.还是比较有用.附带其它宏定义参数,一起记录之.

1.    ...和__VA_ARGS__


看看msdn上给得例子吧

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #define EMPTY  
  3.   
  4. #define CHECK1(x, ...) if (!(x)) { printf(__VA_ARGS__); }  
  5. #define CHECK2(x, ...) if ((x)) { printf(__VA_ARGS__); }  
  6. #define CHECK3(...) { printf(__VA_ARGS__); }  
  7. #define MACRO(s, ...) printf(s, __VA_ARGS__)  
  8.   
  9. int main() {  
  10.     CHECK1(0, "here %s %s %s", "are", "some", "varargs1(1)\n");  
  11.     CHECK1(1, "here %s %s %s", "are", "some", "varargs1(2)\n");   // won't print  
  12.       
  13.     CHECK2(0, "here %s %s %s", "are", "some", "varargs2(3)\n");   // won't print  
  14.     CHECK2(1, "here %s %s %s", "are", "some", "varargs2(4)\n");  
  15.       
  16.     // always invokes printf in the macro  
  17.     CHECK3("here %s %s %s", "are", "some", "varargs3(5)\n");  
  18.       
  19.     MACRO("hello, world\n");  
  20.     // MACRO("error\n", EMPTY);   would cause C2059  
  21. }  

我们看看输出:

here are some varargs1(1)
here are some varargs2(4)
here are some varargs3(5)
hello, world

是不是很有趣,以后我们如果在一些情况下要定义自己的打印函数就特别方便了

比如我们要在A条件成立,且B条件不成立的情况下打印输出

可以做如下定义:

[cpp]  view plain  copy
  1. #define CHECK1(A,B,...) if ((A)&&!(B)) { printf(__VA_ARGS__); }  // ... 用 __VA_ARGS__ 来代替

记住一点...只能代替后面的参数,如下定义可行不通:

[cpp]  view plain  copy
  1. #define W(x,...,y)  

是不是很像C++的函数的默认参数一样.





宏中"#"和"##"的用法


一、一般用法 
我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起. 
用法: 
#include<cstdio> 
#include<climits> 
using namespace std; 
#define STR(s)     #s 
#define CONS(a,b)  int(a##e##b) 

int main() 
{ 
    printf(STR(vck));           // 输出字符串"vck" 
    printf("%d ", CONS(2,3));  // 2e3 输出:2000 
    return 0; 
} 
二、当宏参数是另一个宏的时候 
需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开.
1, 非'#'和'##'的情况 
#define TOW      (2) 
#define MUL(a,b) (a*b) 
printf("%d*%d=%d ", TOW, TOW, MUL(TOW,TOW)); 
这行的宏会被展开为: 
printf("%d*%d=%d ", (2), (2), ((2)*(2))); 
MUL里的参数TOW会被展开为(2). 

2, 当有'#'或'##'的时候 
#define A          (2) 
#define STR(s)     #s 
#define CONS(a,b)  int(a##e##b) 
printf("int max: %s",  STR(INT_MAX));    // INT_MAX #include<climits>
这行会被展开为: 
printf("int max: %s", "INT_MAX"); 
printf("%s", CONS(A, A));               // compile error 
这一行则是: 
printf("%s", int(AeA)); 
INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏. 
加这层宏的用意是把所有宏的参数在这层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数. 
#define A           (2)
#define _STR(s)     #s 
#define STR(s)      _STR(s)          // 转换宏 
#define _CONS(a,b)  int(a##e##b) 
#define CONS(a,b)   _CONS(a,b)       // 转换宏 
printf("int max: %s", STR(INT_MAX));          // INT_MAX,int型的最大值,为一个变量 #include<climits>
输出为: int max: 0x7fffffff 
STR(INT_MAX) -->  _STR(0x7fffffff) 然后再转换成字符串; 
printf("%d", CONS(A, A)); 
输出为:200CONS(A, A)  -->  _CONS((2), (2))  --> int((2)e(2))

三、'#'和'##'的一些应用特例 
1、合并匿名变量名 
#define  ___ANONYMOUS1(type, var, line)  type  var##line
#define  __ANONYMOUS0(type, line)  ___ANONYMOUS1(type, _anonymous, line)
#define  ANONYMOUS(type)  __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int);  即: static int _anonymous70;  70表示该行行号; 
第一层:ANONYMOUS(static int);  -->  __ANONYMOUS0(static int, __LINE__);
第二层:                        -->  ___ANONYMOUS1(static int, _anonymous, 70);
第三层:                        -->  static int  _anonymous70;
即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开; 
2、填充结构 
#define  FILL(a)   {a, #a} 
enum IDD{OPEN, CLOSE}; 
typedef struct MSG{ 
  IDD id; 
  const char * msg; 
}MSG; 

MSG _msg[] = {FILL(OPEN), FILL(CLOSE)}; 
相当于: 
MSG _msg[] = {{OPEN, "OPEN"}, 
              {CLOSE, "CLOSE"}};

3、记录文件名 
#define  _GET_FILE_NAME(f)   #f 
#define  GET_FILE_NAME(f)    _GET_FILE_NAME(f) 
static char  FILE_NAME[] = GET_FILE_NAME(__FILE__); 

4、得到一个数值类型所对应的字符串缓冲大小 
#define  _TYPE_BUF_SIZE(type)  sizeof #type 
#define  TYPE_BUF_SIZE(type)   _TYPE_BUF_SIZE(type) 
char  buf[TYPE_BUF_SIZE(INT_MAX)]; 
     -->  char  buf[_TYPE_BUF_SIZE(0x7fffffff)];
     -->  char  buf[sizeof "0x7fffffff"];
这里相当于: 
char  buf[11];


使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起




2.   #

这个特殊的宏定义参数也特别有用.#作为一个预处理运算符,它可以把语言符号字符串化(stringizing).例如我们定义的变量等.

看一个简单的例子:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #define TEST(x) printf("square of  " #x " is %d.\n",(x)*(x))  
  3. void main()  
  4. {  
  5.     int y =4;  
  6.     TEST(y);  
  7.     TEST(6-3);  
  8.     TEST(y+3);  
  9. }  

输出结果:

[cpp]  view plain  copy
  1. square of  y is 16.  
  2. square of  6-3 is 9.  
  3. square of  y+3 is 49.  

3.   ##

##运算符可以用于类函数宏的替换部分.##还可以用于类对象宏的替换部分.这个运算符可以把两个语言符号组合成单个语言符号.

例如:

[cpp]  view plain  copy
  1. #define XNAME(n) x##n  
  2. 执行宏调用:  
  3. int XNAME(4)  
  4. 展开后:  
  5. x4;//相当于直接定义 int x4 这里也就体现了两个语言符号的拼接.不过在android经常看到有关##拼接语言符号的情况.  

20,宏定义防止使用是错误 
用小括号包含。 
例如:#define ADD(a,b) (a+b) 
用do{}while(0)语句包含多语句防止错误 
例如:#difne DO(a,b) a+b;\ 
                   a++;
应用时:if(….) 
          DO(a,b); //产生错误 
        else 
解决方法: #difne DO(a,b) do{a+b;\ 
                   a++;}while(0)

 

宏中"#"和"##"的用法

一、一般用法 
我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起. 
用法: 
#include<cstdio> 
#include<climits> 
using namespace std; 
#define STR(s)     #s 
#define CONS(a,b)  int(a##e##b) 

int main() 
{ 
    printf(STR(vck));           // 输出字符串"vck" 
    printf("%d ", CONS(2,3));  // 2e3 输出:2000 
    return 0; 
} 
二、当宏参数是另一个宏的时候 
需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开.
1, 非'#'和'##'的情况 
#define TOW      (2) 
#define MUL(a,b) (a*b) 
printf("%d*%d=%d ", TOW, TOW, MUL(TOW,TOW)); 
这行的宏会被展开为: 
printf("%d*%d=%d ", (2), (2), ((2)*(2))); 
MUL里的参数TOW会被展开为(2). 

2, 当有'#'或'##'的时候 
#define A          (2) 
#define STR(s)     #s 
#define CONS(a,b)  int(a##e##b) 
printf("int max: %s",  STR(INT_MAX));    // INT_MAX #include<climits>
这行会被展开为: 
printf("int max: %s", "INT_MAX"); 
printf("%s", CONS(A, A));               // compile error 
这一行则是: 
printf("%s", int(AeA)); 
INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏. 
加这层宏的用意是把所有宏的参数在这层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数. 
#define A           (2)
#define _STR(s)     #s 
#define STR(s)      _STR(s)          // 转换宏 
#define _CONS(a,b)  int(a##e##b) 
#define CONS(a,b)   _CONS(a,b)       // 转换宏 
printf("int max: %s", STR(INT_MAX));          // INT_MAX,int型的最大值,为一个变量 #include<climits>
输出为: int max: 0x7fffffff 
STR(INT_MAX) -->  _STR(0x7fffffff) 然后再转换成字符串; 
printf("%d", CONS(A, A)); 
输出为:200CONS(A, A)  -->  _CONS((2), (2))  --> int((2)e(2))

三、'#'和'##'的一些应用特例 
1、合并匿名变量名 
#define  ___ANONYMOUS1(type, var, line)  type  var##line
#define  __ANONYMOUS0(type, line)  ___ANONYMOUS1(type, _anonymous, line)
#define  ANONYMOUS(type)  __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int);  即: static int _anonymous70;  70表示该行行号; 
第一层:ANONYMOUS(static int);  -->  __ANONYMOUS0(static int, __LINE__);
第二层:                        -->  ___ANONYMOUS1(static int, _anonymous, 70);
第三层:                        -->  static int  _anonymous70;
即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开; 
2、填充结构 
#define  FILL(a)   {a, #a} 
enum IDD{OPEN, CLOSE}; 
typedef struct MSG{ 
  IDD id; 
  const char * msg; 
}MSG; 

MSG _msg[] = {FILL(OPEN), FILL(CLOSE)}; 
相当于: 
MSG _msg[] = {{OPEN, "OPEN"}, 
              {CLOSE, "CLOSE"}};

3、记录文件名 
#define  _GET_FILE_NAME(f)   #f 
#define  GET_FILE_NAME(f)    _GET_FILE_NAME(f) 
static char  FILE_NAME[] = GET_FILE_NAME(__FILE__); 

4、得到一个数值类型所对应的字符串缓冲大小 
#define  _TYPE_BUF_SIZE(type)  sizeof #type 
#define  TYPE_BUF_SIZE(type)   _TYPE_BUF_SIZE(type) 
char  buf[TYPE_BUF_SIZE(INT_MAX)]; 
     -->  char  buf[_TYPE_BUF_SIZE(0x7fffffff)];
     -->  char  buf[sizeof "0x7fffffff"];
这里相当于: 
char  buf[11];

手把手教你学Simulink--基于未来出行解决方案的场景实例:电动飞行汽车(eVTOL)的动力系统建模与仿真 电动垂直起降飞行器零道路拥堵:三维空间飞行绿色低碳:全电驱动,零排放快速响应:城市内15分钟直达低噪音:相比直升机↓20dB📊 预测:2030年全球eVTOL市场规模将超300亿美元,中国、美国、欧洲加速适航认证。本文通过,构建了电动飞行汽车(eVTOL)动力系统高压电池系统建模电力电子(DC-DC、逆变器)实现PMSM电机+螺旋桨推进系统搭建完整飞行任务动态仿真性能KPI评估与优化。 阅读详情

相关推荐

ComfyUI入门学习-教程篇

深入的 ComfyUI 教程,不同功能节点的详细指南。一个比较有意思的Comfyui教程,动画式学习。从安装到基础教程、高级教程再到各种工作流。非常全面,有教程有搭建好的工作流。

wincyfu的博客 1324

stdarg.h 详解

stdarg.h 简介 stdarg 由 standard argument 简化而来,该头文件的主要目的为让函数能够接受可变参数。 该头文件中声明了一个类型 va_list 和三个宏函数 va_start,va_arg 和 va_end。 数据类型(Types): 数据类型 va_list 用来保存宏 va_arg 与宏 va_end 所需...

ultraji的博客 1967

从零搭建openEuler ARM64开发测试环境:手把手教你用libvirt+KVM配置虚拟机(含VNC远程安装)

本文详细介绍了如何在ARM64平台上从零搭建openEuler开发测试环境,涵盖libvirt和KVM配置虚拟机的完整流程,包括环境准备、存储配置、网络优化、XML配置详解以及VNC远程安装。特别针对华为欧拉(openEuler)系统进行了优化,帮助开发者快速构建高效的ARM架构虚拟化环境。

weixin_30545945的博客 586

C语言 __VA_ARGS__ - C语言零基础入门教程

目录 一.__VA_ARGS__简介 二.__VA_ARGS__使用 1.__VA_ARGS__宏输出字符串常量 2.__VA_ARGS__宏输出可变参数 三.__VA_ARGS__缺点 1.仅仅只支持字符串常量,不支持可变参数 2.仅仅只支持可变参数,不支持字符串常量 四.猜你喜欢 零基础 C/C++ 学习路线推荐 : C/C++ 学习目录 >> C 语言基础入门 一.__VA_ARGS__简介 __VA_ARGS__ 是一个可变参数的宏,是新的 C99 规范中新增的,目前

猿说编程 2653

英飞凌AURIX™ TC3xx系列MCU的ISO 26262安全机制实战:从安全手册到代码集成的避坑指南

本文深入探讨英飞凌AURIX™ TC3xx系列MCU的ISO 26262安全机制实战应用,重点解析锁步核配置、ECC策略优化及SafeTlib库集成中的关键问题。通过代码示例和配置建议,帮助开发者实现ASIL D级别安全性,规避常见陷阱,提升汽车电子系统的功能安全合规性。

weixin_42558093的博客 403

Raid5损坏两块盘

  以下为浪潮Inspur NF5460M4型号服务器故障处理过程: 设备raid5组损坏两块硬,并raid组处理离线状态,情况如图:     由于硬盘数据比较重要,面对此情况不要慌我们尽量做到安全保守的保证数据尽可能完整,以下为处理方法,(遇raid5两块盘出现问题处理方法)   以下截图均为遇相同问题的解决图案,并非现场截图。 Ctrl+H 进WebBios界面,发现两...

chen_lipeng的博客 2万+

C语言预处理# ## ... _ _VA_ARGS_ _以及其他的使用

1.# 假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字符串。该过程称为字符串化 在使用#define定义宏时,可使用操作符#在字符串中输出实参。Eg:#define AREA(x,y) printf(“长为“#x”,宽为“#y”的长方形的面积:%d\...

richu123的专栏 2646

SpringBoot 系列教程(十二):给大家推荐20个SpringBoot开源项目,私活必备

基于 SpringBoot2.x + Shiro 实现了用户管理、个人微信、支付宝支付,内置扫码支付、后台回调、订单查询、限流等一系列功能

Thinkingcao的专栏 2839

css中井号,点,星号的区别和应用实例。

在css文件中,经常会看到#号、*号和“.点”,对于没有一点css阅读、开发经验的朋友来讲,这几个符号所代表的意思是什么、有什么作用,肯定是一点都不知道,那么今天我们就一起来认识一下这3个符号在css中的共同点和区别。 “#(井)号”、“*号”、“.点”的相同点: 他们有着一个共同点, 就是都是用来控制网页中元素的css代码的一部分,没有了这几个符号,css代码就无法控制网页元素的显示布局,页

xge的专栏 1万+

数字逻辑Fundamentals of Digital Logic with Verilog Design | 3rd Edition Solutins Chapter 3(step by step)

第三章3-1~3-27全部习题解析和代码模块

weixin_62651190的博客 1946

激光雷达点云异常现象详解:鬼影、高反膨胀与拖点

现象核心外观主要成因核心硬件对策核心算法对策鬼影位置错误,出现"复制"的物体镜面反射/光学元件间杂散光物理光阑、抗反射涂层多帧时序一致性检验、反射率逻辑判断膨胀尺寸变大,轮廓呈毛刺状信号饱和、像素/通道串扰可变增益控制、随机编码序列强度阈值滤波、几何轮廓拟合(剪裁)拖点边缘粘连,出现"阶梯"或"连线"光斑跨立边缘、双回波融合小光斑光学设计、多回波支持邻域深度突变检测、波形脉宽分析这些异常现象是当前激光雷达感知中的核心难点,通常需要硬件物理特性优化与上层感知算法。

Fun_LiDAR的博客 299

宏定义中的特殊参数(#、##、...和__VA_ARGS__) .

最近在android的某个代码的头文件中发现很多__VA_ARGS__,google一下.还是比较有用.附带其它宏定义参数,一起记录之. 1.    ...和__VA_ARGS__ 看看msdn上给得例子吧 [cpp] view plaincopyprint? #include    #define EMPTY     #define CHECK1(x, ...) 

Tony_Wong的专栏 816

【零基础入门unity游戏开发——2D篇】SpriteEditor图片编辑器

【零基础入门unity游戏开发——2D篇】SpriteEditor图片编辑器

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 2324

java中的宏变量,宏定义中的特殊参数(#、##、...和__VA_ARGS__)

##名称:预处理拼接符,或者称其为宏拼接符作用:用于类似函数的宏的替换部分,还可以用于类似对象的宏的替换部分。##放在宏的替换部分的前面,用于宏展开(即宏替换)后,立即将宏中位于##右边的宏替换部分与该宏中位于##左边的部分相拼接至一个整体。例子1:// 宏定义#define XNAME(n) x##n // 宏调用 int XNAME(4) = 1; // 宏展开(即宏替换)后,我们得到: in...

weixin_42131261的博客 394

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

宏定义中的特殊参数(#、##、...和__VA_ARGS__)+二叉查找树

最近在android的某个代码的头文件中发现很多__VA_ARGS__,google一下.还是比较有用.附带其它宏定义参数,一起记录之. 1.    ...和__VA_ARGS__ 看看msdn上给得例子吧 [cpp] view plaincopyprint? #include   #define EMPTY    #define CHECK1(x, ...) i

u013321328的专栏 3851

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

c语言常用到的几个冷知识

c语言常用到的几个冷知识 一,宏定义中的特殊参数(#、##、...和__VA_ARGS__)      1,“#”这个特殊的宏定义参数也特别有用.#作为一个预处理运算符,它可以把语言符号字符串化(stringizing).例如我们定义的变量等.简单的说就是把定义值变成字符串,常用于打印;         #define StringInt(x) #x         int test()

chengf223的博客 1168

c语言常用到的,c语言常用到的几个冷知识

c语言常用到的几个冷知识一,宏定义中的特殊参数(#、##、...和__VA_ARGS__)1,“#”这个特殊的宏定义参数也特别有用.#作为一个预处理运算符,它可以把语言符号字符串化(stringizing).例如我们定义的变量等.简单的说就是把定义值变成字符串,常用于打印;#define StringInt(x) #xint test(){printf("test int is %s\n",Str...

weixin_39653442的博客 179

C语言-自定义格式化输出

C语言-自定义格式化输出。

三皮仔 712

C/C++宏定义中可变参数 __VA_ARGS__ 的理解

用可变参数宏(variadic macros)传递可变参数表 你可能很熟悉在函数中使用可变参数表,如: void printf(const char* format, ...); 一直以来,可变参数表还是只能应用在真正的函数中,不能使用在宏中。 C99编译器标准终于改变了这种局面,它允许你可以定义可变参数宏(variadic macros),这样你就可以使用拥有可以变化的参数表的宏。可...

lijian2017的博客 8870

c++ 宏中的... , __VA_ARGS__, __COUNTER__, #、##、#@

新的C99规范支持了可变参数的宏,省略号表示可变的参数,而__VA_ARGS__一般用于宏的右边表示被省略的参数。把宏字符串话,换句话说就是把宏两两边加上“”,例如。例如我们新建一个文件test_macro.cc。然后下面的命令做预编译。即可得到宏的真实定义。

kangshuangzhu的博客 576

可变参数宏__VA_ARGS__和...

__VA_ARGS__ 是一个可变参数的宏(gcc支持)。实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。这样预定义宏_ _VA_ARGS_ _就可以被用在替换部分中,替换省略号所代表的字符串。加##用来支持0个可变参数的情况。 测试代码: #include #define PRINT_DBG(debug, ...)\ {\ if(debug) {\ ...

fanrey123的博客 391

C++宏(Macro)的各种玩法

本文转载于http://orzz.org/cxx-macro-play/ 一、普通玩法 1. 基本功能 定义一个宏来替换字符串: #define MACRO_PRINTF printf 这样我们使用MACRO_PRINTF就如同printf一样。 我们还可以像这样定义一个宏名称,但不写任何东西: #define MACRO_NAME 然后像这样通过预定义的开关来调整代码实现: #ifdef ...

freshman94的博客 1万+

CSS中#和.的区别

CSS中#和.的区别 大多数时候,写css都会用.xxx这样的写法。 偶尔会看见使用#yyy这样的写法。 #yyy 是针对id的,而.xxx就如大家常用的那样,是指向class的。 使用如下: <div id="xxx"></div> <div class="yyy"></div> 如果出现这种写法,id优于class <div id="xxx" class="yyy"></div> 针对class写更加灵活,一个div可以有多个c

Nicker_的专栏 5627

C/C++常用宏定义,注意事项,宏中#和##的用法

总结下宏和函数的不同之处,以供大家写代码时使用,这段总结摘自《C和指针》一书。 当然宏定义非常重要的,它可以帮助我们防止出错,提高代码的可移植性和可读性等。 下面列举一些成熟软件中常用得宏定义 1,防止一个头文件被重复包含 #ifndef COM

游戏开发 1万+
上一篇: 音视频封装格式、编码格式知识
下一篇: 使用SecureCRT连接虚拟机
tkp2014
博客等级 码龄12年 137粉丝 241原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值