C语言中宏定义之 ## 用于可变参数

GCC 支持复杂的宏,它使用一种不同的语法,使你可以给可变参数一个名字,如同其它参数一样,比如:

引用
#define debug(format, args...) fprintf(stderr, format, args)


这种定义可读性更强,也更容易描述。完整测试代码:

引用
#include <stdio.h>

#define debug(format, args...) fprintf(stderr, format, args)

int main()
{
    char a[20] = "hello world\n";
    int i = 10;
    debug("i = %d, %s", i, a);

    return 0;
}


运行输出

引用
beyes@linux-beyes:~/C/micro> ./mic.exe 
i = 10, hello world


但是上面的定义仍存在一点问题,如果把上面的代码改为下面的:

引用
#include <stdio.h>

#define debug(format, args...) fprintf(stderr, format, args)

int main()
{

    debug("hello world\n");

    return 0;
}


那么在编译时会提示以下错误

引用
beyes@linux-beyes:~/C/micro> gcc -g mic.c -o mic.exe
mic.c: In function ‘main’:
mic.c:10: error: expected expression before ‘)’ token


提示缺少右边括号。这是因为,当宏展开后,"hello world\n" 代入 format,然而,在其后还紧跟着一个逗号,但是这个逗号后面是期望有 args 参数的,但这里却没有,所以宏不能展开完全,故而无法编译通过。那么,再改一下宏定义:

引用
#include <stdio.h>

#define debug(format, args...) fprintf(stderr, format, ##args)

int main()
{
    debug("hello world\n");

    return 0;
}


这时候,再编译运行及输出:

引用
beyes@linux-beyes:~/C/micro> gcc -g mic.c -o mic.exe
beyes@linux-beyes:~/C/micro> ./mic.exe
hello world


编译通过,并正常输出。上面的代码,在 fprintf() 中的 args 前面加了两个 # 号 ##。
## 号的作用是
如果可变参数部分( args...) 被忽略或为空,那么 "##" 操作会使预处理器 (preprocessor) 去掉它前面的那个逗号。如果在调用宏时,确实提供了一些可变参数,GNU C 也会正常工作,它会把这些可变参数放在逗号的后面;如果没有提供,它就会自动去掉前面的逗号,使宏结束展开 ---- 补充完右边括号。

另外,假如按照 C99 的定义来用,改宏为:

引用
#define debug(format, args...) fprintf(stderr, format, ##__VA_ARGS__)


那么编译会出错:

引用
beyes@linux-beyes:~/C/micro> gcc -g mic.c -o mic.exe
mic.c:3:58: warning: __VA_ARGS__ can only appear in the expansion of a C99 variadic macro
mic.c:9:1: error: pasting "," and "__VA_ARGS__" does not give a valid preprocessing token
mic.c: In function ‘main’:
mic.c:9: error: ‘__VA_ARGS__’ undeclared (first use in this function)
mic.c:9: error: (Each undeclared identifier is reported only once
mic.c:9: error: for each function it appears in.)


原因在于,args... 和 ##__VA_ARGS__ 是不匹配的,正确的匹配应该是:

引用
#define debug(format, ...) fprintf(stderr, format, ##__VA_ARGS__)


注意,... 省略号对应的就是 __VA_ARGS__

一般的,定义可变参数宏的一个流行方法,形如:

引用
#define DEBUG(args) (printf("DEBUG: "), printf args)
if(n != 0) DEBUG(("n is %d\n", n));


这个方法的一个缺点是,要记住一对额外的括弧。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。