C预处理器中的嵌套##运算符

3

案例1)

#define CONCATENATE(x,y) x##y

CONCATENATE(a,CONCATENATE(b,c))的结果是aCONCATENATE(b,c)。

案例2)

 #define CONCATENATE(x,y) x##y
 #define CONCATENATE2(x,y) CONCATENATE(x,y)

CONCATENATE2(a,CONCATENATE2(b,c))将会得到abc。

为什么情况1不起作用?而情况2可以?
请按步骤解释。


你的意思是第二个用例应该是:CONCATENATE2(a, CONCATENATE2(b, c))吗? - R Sahu
@RSahu 是的,我编辑了问题。感谢您的提醒。 - chanzerre
2个回答

3

GCC文档这样解释:

宏参数在替换到宏体中之前,会被完全宏展开,除非它们被字符串化或与其他标记粘合

(强调添加)

另一方面,##(标记粘合)运算符的操作数在粘合在一起之前不会被宏展开。因此,给定以下内容:

CONCATENATE(a,CONCATENATE(b,c))

预处理器在扩展外部宏的主体之前不会扩展CONCATENATE(b,c),因为它是##的操作数。预处理器在重新扫描更多要扩展的宏之前执行标记粘合,因此:

a ## CONCATENATE(b,c)

变成

aCONCATENATE(b,c)

在重新扫描之前,并且没有宏aCONCATENATE(但如果有,那么它将被展开)。
另一方面,使用
CONCATENATE2(a,CONCATENATE2(b,c)),

参数CONCATENATE2(b,c)不是##(或#)运算符的操作数,因此在被替换到宏体之前会被展开,最终产生以下结果:

CONCATENATE(a, bc)

作为外部宏的第一次扩展。之后会再次进行扫描以进行进一步的扩展,最终得到结果。
abc

2
当宏被自我引用(或循环引用)时,比如你在CONCATENATE中所做的那样,它们不会递归展开。这就是为什么:CONCATENATE(a,CONCATENATE(b,c))会得到aCONCATENATE(b,c)
在第二种情况下,CONCATENATE2的展开是在处理CONCATENATE之后进行的。因此,您将得到正确的输出。
大多数情况下,宏的自引用使用都可以正常工作。例外情况是标记粘贴和字符串化。
例如,如果您有:
#define #define foo(x) int x

然后,
foo(foo(x));

翻译:扩展为:
int int x;

如果你有:
#define STR(y) #y

然后,
STR(STR(abcd));

扩展为:

"STR(abcd)";

更多细节:

https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification

如何C预处理器处理循环依赖?

问题不是关于字符串化,而是关于标记粘贴。 - John Bollinger
@JohnBollinger,字符串化只是使用两层宏来实现正确的宏展开和标记粘贴的示例。 - R Sahu
好的,但答案仍然是错误的:自引用宏是指宏名称出现在其定义中,而不是在其参数中。通过参数递归是可行的,这可以很容易地进行测试。 - John Bollinger
@JohnBollinger,我没说这个宏是自我参照的。我说它被用于自我参照的方式中。 - R Sahu
当宏的名称出现在其定义中时,那些明显的自我引用不会被展开是正确的。然而,当宏参数包含对所调用宏的引用时,该规则不适用。通常在这些情况下,参数会被展开。例如:给定#define foo(x) int xfoo(foo(x))展开为int int x,而不是int foo(x)。试试看。 - John Bollinger
@JohnBollinger,感谢您的澄清。我查阅了更多相关资料并更新了我的回答。 - R Sahu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接