定义宏时可以让宏接收可变参数,对于可变参数的定义,标准 C 和 GNU C(GNU 对 C的扩展)是不一样的。
标准 C
标准 C 对于可变参数的定义如下,使用...:
#define eprintf(...) fprintf (stderr, __VA_ARGS__)
在宏定义中,__VA_ARGS__ 代表了所有的可变参数。比如像下面使用宏 eprintf:
eprintf ("%s:%d: ", input_file, lineno)
那么 __VA_ARGS__ 就是 "%s:%d: ", input_file, lineno。
GNU C
GNU C 除了支持标准 C的定义之外,还有自己的一套定义,同样是定义宏 eprintf,GNU C 中可以定义成:
#define eprintf(args...) fprintf (stderr, args)
和标准 C 不同之处在于可变参标志...前面有一个参数名 args,在宏定义里面也没有 __VA_ARGS__,而是直接使用的参数名 args,两者是等价的,但是如果使用 GNU C 的方式,在宏定义中就不可以使用 __VA_ARGS__。
可变参数为空
假如定义了如下宏(不管是使用标准 C 方式还是 GNU C方式都可以,这里使用标准 C 的方式):
#define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)
在标准 C 的环境下进行如下调用,不传递可变参数:
eprintf("success!\n");
宏的扩展将会报错,因为扩展出来后的形式如下:
fprintf(stderr, "success!\n",);
fprintf 最后一个参数后面多了一个逗号,这样将报错。这种情况在标准 C 下无法解决,但是 GNU C可以解决。GNU C 赋予 "##" 另一种特殊意义(不是字符串连接的意义了),如果在可变参数前面加上"##",当可变参数为空时,前面的逗号会被删除:
#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__) // 或者 #define eprintf(format, args...) fprintf(stderr, format, ##args)
当可变参数为空时,宏都会扩展成:
fprintf(stderr, "success!\n");
可以看到最后一个参数后面的逗号被删除了。
Clang 默认使用 GNU11,因此也支持这个功能。