变长参数宏 __VA_ARGS__ 与 ## 的特殊用法

__VA_ARGS__ 与 ## 的特殊用法

你可能很熟悉在函数中使用可变参数列表,如:

// 变长参数的函数
void printf(const char* format, ...);

GNU C 中可以用可变参数宏 (variadic macros) 传递可变参数列表。需要注意的是,并不是所有的编译器都支持可变参数宏。C99 中引入了可变参数宏,因此满足 C99 标准的编译器允许定义可变参数宏,这样你就可以使用可变参数表的宏了。可变参数宏像下面这个样子:

// format 称为 named arguments, ... 称为 variable arguments
#define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)

eprintf ("%s:%d: ", input_file, lineno)
    --> fprintf (stderr, "%s:%d: ", input_file, lineno)

这里 ... 指可变参数。它被表示成零个多个符号。当被调用时,在宏体(macro body)中,... 表示的那些符号序列集合将代替宏体里的 __VA_ARGS__ 标识符。更多的信息可以参考 C 预编译器手册。假如参数个数为 0 的话, 上面的写法 GCC 在宏展开后,会有个多余的逗号。

eprintf("%s")
    --> fprintf(stderr, "%s", )

为了解决这个问题,使用一个特殊的 ## 符号。## 正常情况下在宏里的作用是连接两个字符串。当在 , 和变长参数之间出现时作用将发生变化。当变长参数列表是空的时候,## 前的逗号会被移除。

#define eprintf(format, ...) fprintf (stderr, format, ## __VA_ARGS__)

如果可变参数被忽略或为空,## 操作将使预处理器 (preprocessor) 去除掉它前面的那个逗号。

#define eprintf(format, ...) fprintf(stderr, format, __VA_ARGS__)
#define eprintf_no_comma(format, ...) fprintf(stderr, format, ## __VA_ARGS__)
#define gnu_eprintf(format, args...) fprintf(stderr, format, args)
#define gnu_eprintf_no_comma(format, args...) fprintf(stderr, format, ## args)

/*
fprintf(stderr, "%s", )
fprintf(stderr, "%s")
fprintf(stderr, "%s", )
fprintf(stderr, "%s")
*/
eprintf("%s")
eprintf_no_comma("%s")
gnu_eprintf("%s")
gnu_eprintf_no_comma(%s)

In the ISO C standard of 1999, a macro can be declared to accept a variable number of arguments much as a function can. The syntax for defining the macro is similar to that of a function. GCC has long supported variadic macros, and used a different syntax that allowed you to give a name to the variable arguments just like any other argument. Here is an example:

// GCC 还支持给可变参数一个更具描述性的名字以替换 __VA_ARGS__
/* give variable arguments the name args */
#define eprintf(format, args...) fprintf(stderr, format, args)

/* ISO C 的写法,same as above #define */
#define eprintf(format, ...) fprintf(stderr, format, __VA_ARGS__)

This is in all ways equivalent to the ISO C example above, but arguably more readable and descriptive.

In standard C, you are not allowed to leave the variable argument out entirely; but you are allowed to pass an empty argument. For example, this invocation is invalid in ISO C, because there is no comma after the string:

// 标准 C 中必须使用 , 分割命名参数和可变参数
eprintf("A message")

you should write like this

/* the comma separating the named argument(format) from variable arguments(...) */
eprintf("A message", )

GNU CPP permits you to completely omit the variable arguments in this way. In the above examples, the compiler would complain, though since the expansion of the macro still has the extra comma after the format string.

在可变参数列表空参数时,GNU CPP 允许不写 ,,但是参数最后依然会生成一个 ,。这会导致编译错误。GNU CPP 提供了 ## 标识符来解决这个问题。

To help solve this problem, CPP behaves specially for variable arguments used with the token paste operator, ‘##’. If instead you write

#define eprintf(format, ...) fprintf(stderr, format, ## __VA_ARGS__)

and if the variable arguments are omitted or empty, the ## operator causes the preprocessor to remove the comma before it. If you do provide some variable arguments in your macro invocation, GNU CPP does not complain about the paste operation and instead places the variable arguments after the comma. Just like any other pasted macro argument, these arguments are not macro expanded.

Variadic macros became a standard part of the C language with C99. GNU CPP previously supported them with a named variable argument (args..., not ... and __VA_ARGS__), which is still supported for backward compatibility.

Reference

Variadic-Macros

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值