使用VS 2008和GCC时对__VA_ARGS__的不同处理问题

liu*_*liu 7 gcc visual-studio-2008

我试图找出一个问题,因为variadic宏的使用不正常.这是假设宏:

#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);
Run Code Online (Sandbox Code Playgroud)

对于gcc,预处理器将输出

printf("%d %d %d\n", 1, 2, 3)
Run Code Online (Sandbox Code Playgroud)

但对于VS 2008,输出是

printf, “%d %d %d\n”, 1(2, 3);
Run Code Online (Sandbox Code Playgroud)

我怀疑差异是由于对VA_ARGS的不同处理造成的,对于gcc,它首先将表达式扩展为va(printf,"%d%d%d \n",1,2,3),并处理1,2 ,3作为宏va 的VA_ARGS.但对于VS 2008,它将首先将b视为VA_ARGS用于宏va,然后进行扩展.

哪一个是C99可变参数宏的正确解释?或者我的用法属于未定义的行为?

小智 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);
Run Code Online (Sandbox Code Playgroud)

这将在VS 2008上发挥作用,它不会影响gcc


Mic*_*bus 4

查看 ISO/IEC 9899:1999,第 6.10.3.1 章。\n它指出:

\n\n
\n

在识别了调用类函数宏的参数,将进行参数替换。替换列表中的参数,除非前面有 # 或 ## 预处理标记或后面有 ## 预处理标记(见下文),否则在其中包含的所有宏都已展开后将被相应的参数替换。在被替换之前每个参数\xe2\x80\x99s预处理标记被完全宏替换,就好像它们形成了预处理文件的其余部分一样;没有其他可用的预处理标记。

\n
\n\n

因此,在va中,第一个参数c有一个预处理标记__VA_ARGS__,根据本段,在替换为c之前必须先进行宏替换(这仍然没有给出哪个编译器是正确的答案)

\n\n

但后来:

\n\n
\n

替换列表中出现的标识符__VA_ARGS__应被视为参数并且变量参数应形成用于替换它的预处理标记。

\n
\n\n

根据第一个片段,首先识别var的参数。它们是:23printf\xe2\x80\x9c%d %d %d\\n\xe2\x80\x9d1现在发生参数替换。这意味着将采用并替换var的替换列表中的参数。然而,第二个片段指出,该标识符__VA_ARGS__将被视为参数,因此必须将其替换为printf, \xe2\x80\x9c%d %d %d\\n\xe2\x80\x9d, 1。现在参数内部没有宏,因此不会发生进一步的替换,从而导致var(2, 3, printf, \xe2\x80\x9c%d %d %d\\n\xe2\x80\x9d, 1);into扩展va(printf, \xe2\x80\x9c%d %d %d\\n\xe2\x80\x9d, 1, 2, 3);。因为 va 是一个宏,所以它也会被扩展,给出 的结果printf(\xe2\x80\x9c%d %d %d\\n\xe2\x80\x9d, 1, 2, 3);。\n现在如果你采用 VS 2008 的推理,它如何识别va的参数,如果其中一个参数来自__VA_ARGS__var 并且可以包含许多参数?好吧,它被视为va__VA_ARGS__的参数,在我看来这是错误的,因为根据片段一,参数替换仅在调用参数被识别之后才发生。

\n\n

所以在我看来,在var中,预处理器必须首先识别调用宏va的参数,然后开始扩展va。这意味着 gcc 可能就在这里。

\n\n

C 预处理器和串联中还显示了如何按顺序宏替换处理标记,直到没有可以进一步扩展为宏的标识符,或者预处理器发现递归并终止扩展。

\n