替换列表中涉及 ## 运算符的参数化宏
Parameterized macros involving the ## operator in the replacement-list
在我正在阅读的书“C Programming A Modern Approach”中,第 343 页上有一节讨论了一些技巧,您可以使用这些技巧来解决某些缺陷宏。
示例问题描述如下:
#define CONCAT(x,y) x##y
(指令 1)
然后作者解释说,如果使用上述指令,以下代码行将无法按预期运行:
CONCAT(a, CONCAT(b,c))
这行代码将导致 aCONCAT(b,c)
而不是所需的 abc
.
为了解决这个缺点,作者提出了以下解决方法:
#define CONCAT2(x,y) CONCAT(x,y)
(指令 2)
作者解释说指令1和指令2的存在将确保略有不同 代码行 CONCAT2(a, CONCAT2(b,c))
被正确替换为 abc
.
(注意这行代码与原来的代码行不同...CONCAT2
被用来代替CONCAT
。)
有人可以告诉我为什么这会成功执行所需的 objective 吗?据我了解,预处理器将继续扫描预编译代码,直到处理完所有定义的术语。对于给定的扫描,每行更新了多少定义的词?
我认为会发生以下预处理替换流程:
给定 CONCAT2(a, CONCAT2(b,c))
...
第一次传球:CONCAT(a, CONCAT2(b,c))
但是,对于第二次传递,CONCAT
是否扩展为其替换列表表达式?或者 CONCAT2
是否扩展到它的替换列表表达式?在任何一种情况下,似乎我们又一次到达了 aCONCAT2(b,c)
或 CONCAT(a, CONCAT(b,c))
的失败表达式,因此它仍然会失败,就像我们提出的最原始的情况一样。
非常感谢任何帮助!
当预处理器在扫描源代码行时检测到 function-like 宏调用时,它会完全扩展宏的参数,然后将它们替换为宏的替换文本,except 即当参数作为字符串化 (#
) 或 token-pasting (##
) 运算符的操作数出现时,其字面值用于运算.生成的替换文本,带有扩展的参数和替换的任何 #
和 ##
操作的结果,然后被重新扫描以寻找额外的宏来扩展。
因此,...
CONCAT(a, CONCAT(b,c))
... 两个参数的字面值用作 token-pasting 操作的操作数。结果是...
aCONCAT(b,c)
。重新扫描以进一步扩展宏,但 aCONCAT
未定义为宏名称,因此不会发生进一步的宏扩展。
现在考虑...
CONCAT2(a, CONCAT2(b,c))
。 在 CONCAT2
中,两个参数都不是 #
或 ##
的操作数,因此两者在被替换之前完全是 macro-expanded。当然 a
没有变化,但 CONCAT2(b,c)
扩展为 CONCAT(b,c)
,重新扫描后扩展为 bc
。通过将扩展参数值替换为其替换文本,外部 CONCAT2
调用扩展为 ...
CONCAT(a, bc)
。然后在周围源文本的上下文中重新扫描该扩展以进行进一步的宏扩展,产生 ...
abc
。再次重新扫描,但没有进一步的宏扩展要执行,所以这就是最终结果。
在我正在阅读的书“C Programming A Modern Approach”中,第 343 页上有一节讨论了一些技巧,您可以使用这些技巧来解决某些缺陷宏。
示例问题描述如下:
#define CONCAT(x,y) x##y
(指令 1)
然后作者解释说,如果使用上述指令,以下代码行将无法按预期运行:
CONCAT(a, CONCAT(b,c))
这行代码将导致 aCONCAT(b,c)
而不是所需的 abc
.
为了解决这个缺点,作者提出了以下解决方法:
#define CONCAT2(x,y) CONCAT(x,y)
(指令 2)
作者解释说指令1和指令2的存在将确保略有不同 代码行 CONCAT2(a, CONCAT2(b,c))
被正确替换为 abc
.
(注意这行代码与原来的代码行不同...CONCAT2
被用来代替CONCAT
。)
有人可以告诉我为什么这会成功执行所需的 objective 吗?据我了解,预处理器将继续扫描预编译代码,直到处理完所有定义的术语。对于给定的扫描,每行更新了多少定义的词?
我认为会发生以下预处理替换流程:
给定 CONCAT2(a, CONCAT2(b,c))
...
第一次传球:CONCAT(a, CONCAT2(b,c))
但是,对于第二次传递,CONCAT
是否扩展为其替换列表表达式?或者 CONCAT2
是否扩展到它的替换列表表达式?在任何一种情况下,似乎我们又一次到达了 aCONCAT2(b,c)
或 CONCAT(a, CONCAT(b,c))
的失败表达式,因此它仍然会失败,就像我们提出的最原始的情况一样。
非常感谢任何帮助!
当预处理器在扫描源代码行时检测到 function-like 宏调用时,它会完全扩展宏的参数,然后将它们替换为宏的替换文本,except 即当参数作为字符串化 (#
) 或 token-pasting (##
) 运算符的操作数出现时,其字面值用于运算.生成的替换文本,带有扩展的参数和替换的任何 #
和 ##
操作的结果,然后被重新扫描以寻找额外的宏来扩展。
因此,...
CONCAT(a, CONCAT(b,c))
... 两个参数的字面值用作 token-pasting 操作的操作数。结果是...
aCONCAT(b,c)
。重新扫描以进一步扩展宏,但 aCONCAT
未定义为宏名称,因此不会发生进一步的宏扩展。
现在考虑...
CONCAT2(a, CONCAT2(b,c))
。 在 CONCAT2
中,两个参数都不是 #
或 ##
的操作数,因此两者在被替换之前完全是 macro-expanded。当然 a
没有变化,但 CONCAT2(b,c)
扩展为 CONCAT(b,c)
,重新扫描后扩展为 bc
。通过将扩展参数值替换为其替换文本,外部 CONCAT2
调用扩展为 ...
CONCAT(a, bc)
。然后在周围源文本的上下文中重新扫描该扩展以进行进一步的宏扩展,产生 ...
abc
。再次重新扫描,但没有进一步的宏扩展要执行,所以这就是最终结果。