类函数宏的嵌套调用

Nested invocations of function-like macros

考虑以下代码片段:

#define FOO() BAR
#define BAR() FOO

FOO()()()

C 标准告诉我们,在参数替换等之后,重新扫描宏调用产生的预处理标记以获取更多宏名称,忽略生成它们的宏的名称(c99,6.10.3.4 p1-2)

因此,我希望预处理器将片段变成 BAR()(),然后 FOO(),然后停止,因为标记 FOO 是宏 FOO,并且不被识别为宏名称。

但是GCC和clang都给了我结果BAR,说明它实际上又扩大了一次。仅当宏的调用“发生”在参数列表中(宏名称 FOO 不再被忽略)而不是宏名称本身时,这才有意义。这是非常不直观的,我在标准中没有提到它。我错过了什么?

提前致谢!

这是 C 标准中的相关段落:

6.10.3.4 Rescanning and further replacement

1 After all parameters in the replacement list have been substituted and # and ## processing has taken place, all placemarker preprocessing tokens are removed. The resulting preprocessing token sequence is then rescanned, along with all subsequent preprocessing tokens of the source file, for more macro names to replace.

2 If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced. Furthermore, if any nested replacements encounter the name of the macro being replaced, it is not replaced. These nonreplaced macro name preprocessing tokens are no longer available for further replacement even if they are later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.

3 The resulting completely macro-replaced preprocessing token sequence is not processed as a preprocessing directive even if it resembles one, but all pragma unary operator expressions within it are then processed as specified in 6.10.9 below.

例如,如果您写了

#define QQ() QQ
QQ()()()

扩展将只是 QQ()() 因为根据 2) 当在替换列表扫描期间找到 QQ 时,它不会被扩展。

相反,在您的示例中,FOO 未在 FOO() 的替换列表中找到,BAR 后跟 (),这导致它被扩展和反过来 BARBAR() 的替换列表中找不到,但是 FOO 后面的最后一组 () 再次展开。

短语if any nested replacements encounter the name of the macro being replaced, it is not replaced指的是发生在宏参数扩展过程中的替换。在您的示例中,替换是迭代发生的,而不是递归发生的,因此额外的 () 集将导致进一步扩展。

C 预处理器实现 Prosser's blue paint algorithm.

在某个函数符号展开的瞬间,那个符号是painted blue,蓝色的符号不会再展开。

要完全了解 CPP 的工作原理,您必须 google“蓝色油漆”并阅读...