野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 81197|回复: 246

关于 带参宏定义 与 自定义函数 的 汇总

  [复制链接]
发表于 2014-8-13 16:35:05 | 显示全部楼层 |阅读模式
本帖最后由 ii童话Bū说话 于 2014-8-13 17:20 编辑

山外哥的测试题在5楼!自己很菜!听了大神们的解释才懂了!
此处附上答案!
答案:打印输出的是 B=0
解释:
枚举在编译阶段是不参与编译的,所以跳过。
对于未知变量,C语言规定宏处理的时候,未知变量当作 0 来处理。
所以B==0 为真,打印输出 B=0
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////
复制代码
先把总结扔上:
个人理解总结:
以下面代码为例
  1. #define LED1(a)   if (a)\
  2.                          GPIO_SetBits(GPIOC,GPIO_Pin_3);\
  3.                     else \
  4.                          GPIO_ResetBits(GPIOC,GPIO_Pin_3)
复制代码
在编译时进行处理!假设此时a=0;
此后程序运行时等价于
  1. #define LED1(a)    GPIO_SetBits(GPIOC,GPIO_Pin_3);
复制代码
或者看成
  1. #define LED1(a)   /* if (a)\  */                           \
  2.                          GPIO_SetBits(GPIOC,GPIO_Pin_3);   \
  3.                   //  else                                 \
  4.                   //       GPIO_ResetBits(GPIOC,GPIO_Pin_3)
复制代码
无需程序运行时再进行判断!相当于注释!其余部分相当于未存在与程序之中!

对于变量值在程序中不会更改,仅做调试更改的参数,应用#if #elif #else #endif
对于变量在程序中改变的,应用if{}

仅个人观点!不知对错!还望大神们指正!
此外!因各有利弊……
所以什么时候用 带参宏定义 写代码 还是用 自定义函数 写代码还不是很清楚!还望大神们多多指教!!
资料查阅总结后最大感触!!!!自己好菜…………



以下内容摘自百度文档等各处,出处不祥!仅对各个内容作了些汇总!
  1. ////////////////////////////////////////////////////////////////////////////////////////////////////
复制代码
普及
c语言 中 反斜杠( \)的作用:语义上表示,下一行是上一行的延续。也就是同一行。
当你的代码一行写的时候会太长,需要分行方便显示时,但代码又不能分行时,例如这里的宏定义,只能在一行定义好,那样就可以用过在结尾添加 反斜杠( \) 来换行。表示 接着下一行,就是例子中的整个 if-else 语句都被 反斜杠( \) 连接在同一行,所以替换后就仅仅一行而已。
注意,反斜杠( \) 后面不能有任何字符,包括空格。
例:
  1. #define LED1(a)   if (a)\
  2.                          GPIO_SetBits(GPIOC,GPIO_Pin_3);\
  3.                     else \
  4.                          GPIO_ResetBits(GPIOC,GPIO_Pin_3)
复制代码
带参宏定义与普通函数
带参宏定义函数调用

处理时间

只在预编译时处理在程序运行时处理
操作内容制作简单的字符替换,不进行值的传递,也没有返回值和类型的概念先求表达式的值,然后进行形参实参结合 的数据传递,返回一个值,有类型的概念。


函数式宏定义:#define MAX(a,b) ((a)>(b)?(a)b))
普通函数     : MAX(a,b) { return a>b?a:b;}
(1)函数式宏定义的参数没有类型,预处理器只负责做形式上的替换,而不做参数类型检查,所以传参时要格外小心。
(2)调用真正函数的代码和调用函数式宏定义的代码编译生成的指令不同。
  如果MAX是个普通函数,那么它的函数体return a > b ? a : b; 要编译生成指令,代码中出现的每次调用也要编译生成传参指令和call指令。
而如果MAX是个函数式宏定义,这个宏定义本身倒不必编译生成指令,但是代码中出现的每次调用编译生成的指令都相当于一个函数体,而不是简单的几条传参指令和call指令。
所以,使用函数式宏定义编译生成的目标文件会比较大。
(3)函数式宏定义要注意格式,尤其是括号。
  如果上面的函数式宏定义写成 #define MAX(a, b) (a>b?a:b),省去内层括号,则宏展开就成了k = (i&0x0f>j&0x0f?i&0x0f:j&0x0f),运算的优先级就错了。同样道理,这个宏定义的外层括号也是不能省的。若函数中是宏替换为 ++MAX(a,b),则宏展开就成了 ++(a)>(b)?(a)b),运算优先级也是错了。
(4)若函数参数为表达式,则普通函数的调用与函数式宏定义的替换过程是不一样的。
  普通函数调用时先求实参表达式的值再传给形参,如果实参表达式有Side Effect,那么这些SideEffect只发生一次。例如MAX(++a, ++b),如果MAX是普通函数,a和b只增加一次。但如果MAX函数式宏定义,则要展开成k = ((++a)>(++b)?(++a)++b)),a和b就不一定是增加一次还是两次了。所以若参数是表达式,替换函数式宏定义时一定要仔细看好。
注:那么怎样安全的使用函数式宏定义呢?
把可能产生副作用的操作移到宏调用的外面进行
  1. a++;
  2. b++;
  3. k = ((a)>(b)?(a):(b));
复制代码
(5)函数式宏定义往往会导致较低的代码执行效率。
  1. int a[]={9,3,5,2,1,0,8,7,6,4};
  2. int max(n)
  3. {
  4.     return n==0?a[0]:MAX(a[n],max(n-1));
  5. }

  6. int main()
  7. {
  8.     max(9);
  9.     return 0;
  10. }
复制代码
若是普通函数,则通过递归,可取的最大值,时间复杂度为O(n)。但若是函数式宏定义,则宏展开为( a[n]>max(n-1)?a[n]:max(n-1) ),其中max(n-1)被调用了两遍,这样依此递归下去,时间复杂度会很高。
  尽管函数式宏定义和普通函数相比有很多缺点,但只要小心使用还是会显著提高代码的执行效率,毕竟省去了分配和释放栈帧、传参、传返回值等一系列工作。
因此那些简短并且被频繁调用的函数经常用函数式宏定义来代替实现。

宏定义

1.#define指令
#define预处理指令是用来定义宏的。该指令最简单的格式是:首先声明一个标识符,然后给出这个标识符代表的代码。在后面的源代码中,就用这些代码来替代该标识符。这种宏把程序中要用到的一些全局值提取出来,赋给一些记忆标识符。
  1. #defineMAX_NUM10

  2. intarray[MAX_NUM];

  3. for(i=0;i<MAX_NUM;i++)

  4. /*……*/
复制代码
在这个例子中,对于阅读该程序的人来说,符号MAX_NUM就有特定的含义,它代表的值给出了数组所能容纳的最大元素数目。程序中可以多次使用这个值。作为一种约定,习惯上总是全部用大写字母来定义宏,这样易于把程序宏的宏标识符和一般变量标识符区别开来。如果想要改变数组的大小,只需要更改宏定义并重新编译程序即可。
宏表示的值可以是一个常量表达式,其中允许包括前面已经定义的宏标识符。
例如:
  1. #define ONE1

  2. #define TWO2

  3. #define
  4. THREE(ONE+TWO)
复制代码
注意上面的宏定义使用了括号。尽管它们并不是必须的。但出于谨慎考虑,还是应该加上括号的。
例如:
  1. six=THREE*TWO;
复制代码
预处理过程把上面的一行代码转换成:
  1. six=(ONE+TWO)*TWO;
复制代码
如果没有那个括号,就转换成six=ONE+TWO*TWO;了。
宏还可以代表一个字符串常量,
例如:
  1. #define VERSION "Version1.0Copyright(c)2003"
复制代码


2.带参数的#define指令
带参数的宏和函数调用看起来有些相似。看一个例子:
#defineCube(x)(x)*(x)*(x)
可以时任何数字表达式甚至函数调用来代替参数x。这里再次提醒大家注意括号的使用。
宏展开后完全包含在一对括号中,而且参数也包含在括号中,这样就保证了宏和参数的完整性。
看一个用法:
  1. intnum=8+2;
  2. volume=Cube(num);
复制代码
展开后为
(8+2)*(8+2)*(8+2);
如果没有那些括号就变为8+2*8+2*8+2了。
下面的用法是不安全的:
  1. volume=Cube(num++);
复制代码
如果Cube是一个函数,上面的写法是可以理解的。但是,因为Cube是一个宏,所以会产生副作用。这里的参数不是简单的表达式,它们将产生意想不到的结果。
它们展开后是这样的:
  1. volume=(num++)*(num++)*(num++);
复制代码
很显然,结果是10*11*12,而不是10*10*10;那么怎样安全的使用Cube宏呢?
必须把可能产生副作用的操作移到宏调用的外面进行
  1. int num=8+2;
  2. volume=Cube(num);
  3. num++;
复制代码


3.#运算符
出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#称为字符串化运算符。例如:
  1. #definePASTE(n) "adhfkj"#n
  2. main()
  3. {
  4.     printf("%s\n",PASTE(15));
  5. }
复制代码
宏定义中的#运算符告诉预处理程序,把源代码中任何传递给该宏的参数转换成一个字符串。所以输出应该是adhfkj15。


4.##运算符
##运算符用于把参数连接到一起。预处理程序把出现在##两侧的参数合并成一个符号。
看下面的例子:
  1. #defineNUM(a,b,c) a##b##c

  2. #defineSTR(a,b,c)
  3. a##b##c

  4. main()

  5. {

  6.     printf("%d\n",NUM(1,2,3));

  7.     printf("%s\n",STR("aa","bb","cc"));

  8. }
复制代码
123
aabbcc
千万别担心,除非需要或者宏的用法恰好和手头的工作相关,否则很少有程序员会知道##运算符。绝大多数程序员从来没用过它。

宏定义防止 使用是错误
用小括号包含。
例如:#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)












回复

使用道具 举报

 楼主| 发表于 2014-8-13 16:35:26 | 显示全部楼层
条件编译指令
条件编译指令将决定那些代码被编译,而哪些是不被编译的。可以根据表达式的值或者某个特定的宏是否被定义来确定编译条件。
一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件下才进行编译,即对一部分内容指定编译条件,这就是“条件编译”。(conditional compile)

  条件编译语句排版时,需考虑以下三种位置:

  (1)条件编译语句块与函数定义体之间不存在相互嵌套(主要在(.h)文件中)

  ◆ 条件编译关键字语句顶格左对齐;

  ◆ 所含的#include语句(块) #define语句(块)甚至是被嵌套下级条件编译语句块,按照语句块嵌套的排版方式进行缩进排版 。

  (2)条件编译语句块嵌套在函数体之外(主要在(.c)文件中)

  这种情况下,条件编译语句块不影响函数体

  ◆ 条件编译关键字语句顶格左对齐;

  ◆ 所含的函数体定义无需缩进,依旧按照单个函数体定义的排版方式进行。

  (3)条件编译语句嵌套在函数体内 (主要在(.c)文件中)

  a)当条件编译语句块与被包语句所属的语句块之间没有逻辑路径交叉时,以下两种方式均可

  ◆ 按照语句块嵌套方式进行缩进排版 (推荐);

  ◆ 条件编译语句不影响原先语句块排版,条件编译语句与所包含的关键字语句块左对齐 。

  b)当条件编译语句块与被包语句所属的语句块之间存在逻辑路径交叉时

  ◆ 条件编译语句顶格左对齐,其它语句按照正常顺序排版。


1.#if指令
#if指令检测跟在关键字后的常量表达式。如果表达式为真,则编译后面的代码,知道出现#else、#elif或#endif为止;否则就不编译。
条件编译的形式如下所示(NNN、MMM等都是在某处已经定义为 1 或者 0 的):
  1.   #if NNN
  2.   statement1;
  3.   #elif MMM
  4.   statement2;
  5.   #else
  6.   statement3;
  7.   #endif
复制代码
重要解释:若宏NNN为True则只留下statement1编译;若NNN为False且MMM为True则只编译statement2;若NNN和MMM都为False则编译statement3。
#if是在编译前进行抉择的,而一般的if指令是在程序运行时才做抉择的,因此#if可以提升程序的执行速度,这是两者的重要区别。另外,#if指令还可协助查错。


2.#endif指令
#endif用于终止#if预处理指令。
  1. #defineDEBUG0
  2. main()
  3. {
  4.     #if DEBUG
  5.     printf("Debugging\n");
  6.     #endif
  7.     printf("Running\n");
  8. }
复制代码
由于程序定义DEBUG宏代表0,所以#if条件为假,不编译后面的代码直到#endif,所以程序直接输出Running。
如果去掉#define语句,效果是一样的。


3.#ifdef和#ifndef
  1. #define DEBUG

  2. main()

  3. {

  4.     #ifdef DEBUG

  5.     printf("yes\n");

  6.     #endif

  7.     #ifndef DEBUG

  8.     printf("no\n");

  9.     #endif
  10. }
复制代码
#ifdefined等价于#ifdef;
#if!defined等价于#ifndef
防止一个头文件被重复包含
  1. #ifndef COMDEF_H
  2. #define COMDEF_H  //头文件内容
  3. #endif
复制代码
4.#else指令
#else指令用于某个#if指令之后,当前面的#if指令的条件不为真时,就编译#else后面的代码。
#endif指令将中指上面的条件块。

5.#elif指令
#elif预处理指令综合了#else和#if指令的作用。
  1. #define TWO
  2. main()
  3. {
  4.     #ifdef ONE
  5.     printf("1\n");
  6.     #elif defined TWO
  7.     printf("2\n");
  8.     #else
  9.     printf("3\n");
  10.     #endif
  11. }
复制代码
程序很好理解,最后输出结果是2。

区分:
#ifdef 宏   //若已定义了此宏,则留下#ifdef与#endif间的指令;否则删除之。
#ifndef 宏 //若未定义过此宏,则留下#ifndef与#endif间的指令;否则删除之。
#endif //定义#ifdef及#ifndef的范围。
#undef 宏 //与#defined相反的动作---解除定义。
#else  //可构成#ifdef~#else~#endif结构或#ifndef~#else~#endif结构。
#ifdef与#if的区别
#if 宏:此宏必须已定义,依宏所代表的值来做判断;
#ifdef 宏:此宏不一定已定义,依此宏是否已定义来判断。

组合:
1:情况1:
#ifdef _XXXX
...程序段1...
#else
...程序段2...
#endif
这表明如果标识符_XXXX已被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。
例:
#define NUM
.............
.............
.............
#ifdef NUM
printf("之前NUM有过定义啦! ");
#else
printf("之前NUM没有过定义! ");
#endif
}
如果程序开头有#define NUM这行,即NUM有定义,碰到下面#ifdef NUM的时候,当然执行第一个printf。否则第二个printf将被执行。
我认为,用这种,可以很方便的开启/关闭整个程序的某项特定功能。
2:情况2:
#ifndef _XXXX
...程序段1...
#else
...程序段2...
#endif
这里使用了#ifndef,表示的是if not def。当然是和#ifdef相反的状况(如果没有定义了标识符_XXXX,那么执行程序段1,否则执行程序段2)。例子就不举了。
3:情况3:
#if 常量
...程序段1...
#else
...程序段2...
#endif
这里表示,如果常量为真(非0,随便什么数字,只要不是0),就执行程序段1,否则执行程序段2。
这种方法可以将测试代码加进来。当需要开启测试的时候,只要将常量变1就好了。而不要测试的时候,只要将常量变0。

6.其他一些标准指令
#error指令将使编译器显示一条错误信息,然后停止编译。
#line指令可以改变编译器用来指出警告和错误信息的文件号和行号。
#pragma指令没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦人的警告信息。

关于宏定义的用法
移步http://www.firebbs.cn/forum.php?mod=viewthread&tid=123&highlight=%BA%EA

回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2014-8-13 16:35:49 | 显示全部楼层
自己占个板凳
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-8-13 16:36:09 | 显示全部楼层
还有席梦思也占上!
回复 支持 1 反对 0

使用道具 举报

发表于 2014-8-13 16:40:41 | 显示全部楼层
给你出一个题目,看你会不会。
求打印的结果。不要上机验证结果,自己分析一下答案。
  1. typedef enum
  2. {
  3.     A = 0,
  4.     B = 1,
  5. }TTT;


  6. #if (B == 1)
  7.     printf("B=1");
  8. #elif (B == 0)
  9.     printf("B=0");
  10. #else
  11.     printf("error");
  12. #endif
复制代码
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-8-13 17:21:44 | 显示全部楼层
山外メ雲ジ 发表于 2014-8-13 16:40
给你出一个题目,看你会不会。
求打印的结果。不要上机验证结果,自己分析一下答案。

感谢山外哥指导!膜拜啊!
回复 支持 反对

使用道具 举报

发表于 2014-9-12 20:08:22 | 显示全部楼层
抓紧来看看啊。走过路过,不能错过
回复 支持 反对

使用道具 举报

发表于 2014-9-23 16:59:14 | 显示全部楼层
抢沙发~给哥们顶一个
回复 支持 反对

使用道具 举报

发表于 2014-9-24 08:38:48 | 显示全部楼层
顶一个,学习学习
回复 支持 反对

使用道具 举报

发表于 2014-9-24 18:28:01 | 显示全部楼层
强烈支持,非常感谢哥们
回复 支持 反对

使用道具 举报

发表于 2014-9-26 05:53:08 | 显示全部楼层
帅呆了,赞一个
回复 支持 反对

使用道具 举报

发表于 2014-9-28 18:53:39 | 显示全部楼层
[code=c]#endif 1[/code]
  1. #if 0  
复制代码
像这种代码困惑好久了,特来膜拜
回复 支持 反对

使用道具 举报

发表于 2014-9-28 22:02:01 | 显示全部楼层
强烈支持,非常感谢哥们
回复 支持 反对

使用道具 举报

发表于 2014-9-29 12:35:02 | 显示全部楼层
顶楼主!!!强烈支持,非常感谢哥们强烈支持,非常感谢哥们
回复 支持 反对

使用道具 举报

发表于 2014-10-4 11:27:53 | 显示全部楼层
kankanxian
回复 支持 反对

使用道具 举报

发表于 2014-10-5 10:24:30 | 显示全部楼层
抢沙发~给哥们顶一个强烈支持,非常感谢哥们
回复 支持 反对

使用道具 举报

发表于 2014-10-6 20:07:20 | 显示全部楼层
我不懂,菜菜菜
回复 支持 反对

使用道具 举报

发表于 2014-10-7 10:31:03 | 显示全部楼层
本帖最后由 Ω囿圉Ω 于 2014-10-7 10:33 编辑

条件编译吧,打印出printf("B=1");
???

感觉自己好菜,有木有。。。
回复 支持 反对

使用道具 举报

发表于 2014-10-8 12:37:45 | 显示全部楼层
走过路过,不能错过
回复 支持 反对

使用道具 举报

发表于 2014-10-8 16:58:51 | 显示全部楼层
学习一下了、、。
回复 支持 反对

使用道具 举报

发表于 2014-10-9 18:29:56 | 显示全部楼层
非常感谢楼主的分享,向楼主学习
回复 支持 反对

使用道具 举报

发表于 2014-10-9 22:03:02 | 显示全部楼层
山外メ雲ジ 发表于 2014-8-13 16:40
给你出一个题目,看你会不会。
求打印的结果。不要上机验证结果,自己分析一下答案。

答案是?帅呆了,赞一个
回复 支持 反对

使用道具 举报

发表于 2014-10-10 16:51:22 | 显示全部楼层
好多东西啊
回复 支持 反对

使用道具 举报

发表于 2014-10-10 21:24:42 | 显示全部楼层
抢沙发~给哥们顶一个
回复 支持 反对

使用道具 举报

发表于 2014-10-11 10:04:36 | 显示全部楼层
走过路过,不能错过
回复 支持 反对

使用道具 举报

发表于 2014-10-13 11:49:29 | 显示全部楼层
强烈支持,非常感谢哥们
回复 支持 反对

使用道具 举报

发表于 2014-10-15 16:54:32 | 显示全部楼层
谢谢,看一下~
回复 支持 反对

使用道具 举报

发表于 2014-10-16 17:07:13 | 显示全部楼层
强烈支持,非常感谢哥们
回复 支持 反对

使用道具 举报

发表于 2014-10-16 23:17:58 | 显示全部楼层
抢沙发~给哥们顶一个抢沙发~给哥们顶一个抢沙发~给哥们顶一个抢沙发~给哥们顶一个
回复 支持 反对

使用道具 举报

发表于 2014-10-18 09:22:01 | 显示全部楼层
抢沙发~给哥们顶一个
回复 支持 反对

使用道具 举报

发表于 2014-10-19 21:53:19 | 显示全部楼层
帅呆了,赞一个
回复 支持 反对

使用道具 举报

发表于 2014-10-20 19:31:48 | 显示全部楼层
来看一下好资源了啊!强烈支持,非常感谢哥们
回复 支持 反对

使用道具 举报

发表于 2014-10-20 21:12:14 | 显示全部楼层
B=0,预编译时就把#if那部分删除了
回复 支持 反对

使用道具 举报

发表于 2014-10-21 19:10:55 | 显示全部楼层
抢沙发~给哥们顶一个
回复 支持 反对

使用道具 举报

发表于 2014-10-27 14:42:13 | 显示全部楼层
抢沙发~给哥们顶一个
回复 支持 反对

使用道具 举报

发表于 2014-10-27 21:43:35 | 显示全部楼层
为什么叫山外哥?
回复 支持 反对

使用道具 举报

发表于 2014-11-4 18:08:01 | 显示全部楼层
俺是个新手,先看看在说话
回复 支持 反对

使用道具 举报

发表于 2014-11-15 12:20:13 | 显示全部楼层
11111111111111111111111
回复 支持 反对

使用道具 举报

发表于 2014-11-15 15:27:07 | 显示全部楼层
进来学习学习
回复 支持 反对

使用道具 举报

发表于 2014-11-16 20:16:53 | 显示全部楼层
谢谢楼主,学习了
回复 支持 反对

使用道具 举报

发表于 2014-11-18 21:17:39 | 显示全部楼层
b=1.。。。。。。。。。。。。。
回复

使用道具 举报

发表于 2014-11-18 21:28:51 | 显示全部楼层
好吧,做错了。。
回复 支持 反对

使用道具 举报

发表于 2014-11-19 07:43:55 | 显示全部楼层
嘿,兄弟,
#define LED1(a)   /* if (a)\  */                           \
                         GPIO_SetBits(GPIOC,GPIO_Pin_3);   \
                  //  else                                 \
                  //       GPIO_ResetBits(GPIOC,GPIO_Pin_3)
那如果a=1是怎么回事。。照你这么说不是都屏蔽了,嘛?求指教
回复 支持 反对

使用道具 举报

发表于 2014-11-19 11:26:58 | 显示全部楼层
不错的资料  参与学习  谢谢
回复 支持 反对

使用道具 举报

发表于 2014-11-19 13:12:47 | 显示全部楼层
关于 带参宏定义 与 自定义函数 的 汇总 [修改]
高级模式
回复 支持 反对

使用道具 举报

发表于 2014-11-19 18:04:18 | 显示全部楼层
瞅一眼,取取经
回复 支持 反对

使用道具 举报

发表于 2014-11-19 18:30:32 | 显示全部楼层
学习一下基础知识
回复 支持 反对

使用道具 举报

发表于 2014-11-26 21:58:57 | 显示全部楼层
什么来的,来看看。。。。。
回复 支持 反对

使用道具 举报

发表于 2014-11-27 18:43:30 | 显示全部楼层
.................
回复

使用道具 举报

发表于 2014-12-11 17:26:47 | 显示全部楼层
看看看看看
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-11-24 07:02 , Processed in 0.050335 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表