首页 > 解决方案 > 类函数宏的嵌套调用

问题描述

考虑以下代码片段:

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

FOO()()()

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

因此,我希望预处理器将片段转换为BAR()(), 然后FOO(),然后停止,因为令牌FOO是宏的结果FOO,并且不被识别为宏名称。

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

提前致谢!

标签: cmacroslanguage-lawyerc-preprocessorc99

解决方案


以下是 C 标准的相关段落:

6.10.3.4 重新扫描和进一步更换

1 在替换列表中的所有参数都已被替换并且 # 和 ## 处理已经发生后,所有 placemarker 预处理标记都将被删除。然后重新扫描生成的预处理标记序列以及源文件的所有后续预处理标记,以替换更多宏名称。

2 如果在替换列表的扫描过程中找到被替换的宏的名称(不包括源文件的其余预处理标记),则不会替换它。此外,如果任何嵌套替换遇到被替换的宏的名称,它不会被替换。这些未替换的宏名称预处理标记不再可用于进一步替换,即使它们稍后在该宏名称预处理标记将被替换的上下文中进行(重新)检查。

3 所得到的完全被宏替换的预处理标记序列即使与预处理指令相似,也不会作为预处理指令处理,但其中的所有 pragma 一元运算符表达式随后将按照下面 6.10.9 中的规定进行处理。

例如,如果你写了

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

扩展只是QQ()()因为根据 2) 在QQ替换列表的扫描过程中发现时,它没有被扩展。

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

短语如果任何嵌套替换遇到被替换的宏的名称,它不会被替换是指在宏参数扩展期间发生的替换。在您的示例中,替换是迭代发生的,而不是递归发生的,因此额外的 set()将导致进一步扩展。


推荐阅读