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 ";
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 ");
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
" 代入 format,然而,在其后还紧跟着一个逗号,但是这个逗号后面是期望有 args 参数的,但这里却没有,所以宏不能展开完全,故而无法编译通过。那么,再改一下宏定义:
引用#include <stdio.h>
#define debug(format, args...) fprintf(stderr, format, ##args)
int main()
{
debug("hello world ");
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));
这个方法的一个缺点是,要记住一对额外的括弧。