我正在尝试识别问题,因为使用了可变参数宏。这是假设的宏观:
#define va(c, d, ...) c(d, __VA_ARGS__)
#define var(a, b, ...) va(__VA_ARGS__, a, b)
var(2, 3, printf, “%d %d %d\n”, 1);
对于gcc,预处理器将输出
printf("%d %d %d\n", 1, 2, 3)
但是对于VS 2008,输出是
printf, “%d %d %d\n”, 1(2, 3);
我怀疑差异是由对 VA_ARGS 的不同处理引起的,对于gcc,它首先会将表达式扩展为va(printf,“%d%d%d \ n”,1, 2,3),并将1,2,3视为宏va的 VA_ARGS 。但是对于VS 2008,它首先将b视为宏观va的 VA_ARGS ,然后进行扩展。
哪一个是C99可变参数宏的正确解释?或者我的用法属于未定义的行为?
答案 0 :(得分:6)
有一种简单的方法可以解决这个问题:
#define exp(...) __VA_ARGS__
#define va(c, d, ...) c(d, __VA_ARGS__)
#define var(a, b, ...) exp(va(__VA_ARGS__, a, b))
var(2, 3, printf, “%d %d %d\n”, 1);
这将在VS 2008上发挥作用,它不会影响gcc
答案 1 :(得分:4)
查看ISO / IEC 9899:1999,第6.10.3.1章。 它声明:
后,调用类似函数的宏的参数已经识别,发生了参数替换。替换列表中的参数除非前面带有#或##预处理标记或后跟##预处理标记(见下文),否则将替换为相应的参数 后面包含的所有宏扩大。 在替换之前,每个参数的预处理标记都被完全宏替换,好像它们形成了预处理文件的其余部分;没有其他预处理令牌可供使用。
所以在 va 中,第一个参数 c 有一个预处理标记为__VA_ARGS__
,根据此段,必须将宏替换为< / em>在被替换为 c 之前(仍然没有给出关于哪个编译器是正确的答案)
但后来:
替换列表中出现的标识符
__VA_ARGS__
应被视为参数和变量 参数应构成用于的预处理标记 替换它。
根据第一个片段,首先识别 var 的参数。它们是:2
,3
,printf
,“%d %d %d\n”
和1
。现在进行参数替换。这意味着将获取并替换 var 的替换列表中的参数。但是,第二个片段指出,标识符__VA_ARGS__
将被视为参数,因此必须将其替换为printf, “%d %d %d\n”, 1
。现在参数中没有宏,因此不再进行替换,导致var(2, 3, printf, “%d %d %d\n”, 1);
扩展为va(printf, “%d %d %d\n”, 1, 2, 3);
。因为va是一个宏,它也会被扩展,得到printf(“%d %d %d\n”, 1, 2, 3);
的结果。
现在,如果您采用VS 2008的推理,如果 va 的参数 ,并且可以包含许多参数?好吧,它将__VA_ARGS__
视为 va 的参数,这在我的选择中是错误的,因为根据片段1,参数替换只发生在之后已经确定了调用的参数。
所以在我看来,在 var 中,预处理器必须首先识别调用宏 va 的参数,然后开始扩展 va 。这意味着gcc可能就在这里。
还在C preprocessor and concatenation中显示了处理令牌是如何按顺序进行宏替换的,直到没有可以进一步扩展为宏的标识符,或者预处理器发现递归并终止扩展。