cam*_*cdr 6 c language-lawyer c-preprocessor
您可以使用以下构造迭代预处理器序列:
\n#define A() B\n#define B() A\nA()()()()()\nRun Code Online (Sandbox Code Playgroud)\n扩展到B大多数编译器/预处理器:clang、gcc、tcc、chibicc、SDCC(我无法测试 msvc,因为它在 godbolt 上不起作用,但如果你想测试它,请确保使用该标志/Zc:preprocessor,因为否则预处理器将不合格)。
阅读6.10.3.4似乎表明, 的扩展B发生在 内部A,这将导致 的第二次扩展A不会发生,而是会被漆成蓝色,并且扩展将停止。
\n\n6.10.3.4 重新扫描和进一步替换
\n在替换列表中的所有参数都已被替换并且 # 和 ## 处理已发生后,所有地标预处理标记都将被删除。然后重新扫描生成的预处理标记序列以及源文件的所有后续预处理标记,以查找更多要替换的宏名称。
\n
但附件 J.1 指出,这是否使用嵌套来完成是未指定的行为:
\n\n\n当完全扩展的宏替换列表包含类似函数的宏名称作为其最后一个预处理标记并且源文件中的下一个预处理标记是 ( 时,并且该宏的完全扩展替换以第一个宏的名称和源文件中的下一个预处理标记仍然是 (,无论是否被视为嵌套替换 (6.10.3)。
\n
好吧,公平地说,大多数预处理器都使用非嵌套方法,但是什么允许以下方法工作?
\n#define A() B(\n#define B() A(\nA()))))\nRun Code Online (Sandbox Code Playgroud)\n现在假设前者会给你一个错误,因为“未终止的参数列表调用宏\'B\'”,但是你不希望它扩展到A())),A现在被漆成蓝色,这不应该给出一个错误?
此外,您可以通过检测最后一个右括号来消除错误,表明这似乎也没有使用嵌套,这很奇怪,因为标准在哪里表明这是有效的?
\nSO 上已经有一个类似的问题,但我不明白答案与这个问题有什么关系,因为引用的段落只是谈论参数替换:
\n\n\n6.10.3.1 参数替换
\n在识别出调用类似函数的宏的参数后,将进行参数替换。替换列表中的参数,除非前面有 # 或 ## 预处理标记或后面有 ## 预处理标记(见下文),否则在其中包含的所有宏都被扩展后,将被替换为相应的参数。在被替换之前,每个参数\xe2\x80\x99s 预处理标记都被完全宏替换,就好像它们构成了预处理文件的其余部分一样;没有其他可用的预处理标记。
\n
这是有道理的,因此例如在传递给的#define A(x) x x x参数中只需要在阳光下扩展一次,然后将生成的标记插入到扩展列表中出现的位置。xAx
这也解释了以下行为:
\n#define STR(x) #x\n#define f(x) x\n#define F(x) STR(x(23))\nF(f) // expands to "f(23)"\nRun Code Online (Sandbox Code Playgroud)\n因此,孤立部分指的是它们自身的参数,而不是重新扫描中发生的情况,这在6.10.3.4中有详细说明,这是我最初的标准引用。
\n那么这是怎么回事,我应该如何思考宏观扩张过程呢?
\n根据我对 DR17 的阅读,如果)与左侧的扩展结果连接,则在标准中故意未指定。该行为是未定义的。严格遵守的程序不应该使用它。
为什么宏名称没有涂成蓝色?, https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_017.html,https : //port70.net/%7Ensz/c/c11/n1570.html#6.10.3.4p4