首页 > 解决方案 > 替换列表中涉及## 运算符的参数化宏

问题描述

在我正在阅读的“ 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。)

有人可以告诉我为什么这将成功实现预期的目标吗?据我了解,预处理器将继续扫描预编译的代码,直到处理完所有定义的术语。对于给定的扫描,每行更新多少个定义的单词?

我认为会发生以下预处理替换流程:

鉴于CONCAT2(a, CONCAT2(b,c))...

第一次过关:CONCAT(a, CONCAT2(b,c))

但是,对于第二次传递,是否CONCAT会扩展到其替换列表表达式?还是CONCAT2扩展为它的替换列表表达式?在任何一种情况下,似乎我们再次遇到了aCONCAT2(b,c)or的失败表达式CONCAT(a, CONCAT(b,c)),因此它仍然会失败,就像我们提出的原始案例一样。

任何帮助是极大的赞赏!

标签: cc-preprocessor

解决方案


当预处理器在扫描源代码行时检测到类似函数的宏调用时,它会在将宏的参数替换为宏的替换文本之前完全展开宏的参数,除非参数显示为字符串化 ( #) 或标记粘贴 ( ##) 运算符,其文字值用于操作。然后重新扫描生成的替换文本,其中包含扩展的参数以及替换的任何###操作的结果,以查找要扩展的其他宏。

因此,与...

CONCAT(a, CONCAT(b,c))

...两个参数的文字值都用作标记粘贴操作的操作数。结果是……

aCONCAT(b,c)

. 重新扫描以进一步扩展宏,但aCONCAT未定义为宏名称,因此不会发生进一步的宏扩展。

现在考虑...

CONCAT2(a, CONCAT2(b,c))

. CONCAT2中,两个参数都不是#or的操作数##,因此两者都在被替换之前完全宏扩展。当然a没有改变,但CONCAT2(b,c)扩展为CONCAT(b,c),在重新扫描时扩展为bc。通过将扩展的参数值替换为其替换文本,外部CONCAT2调用扩展为...

CONCAT(a, bc)

. 然后在周围源文本的上下文中重新扫描该扩展,以进行进一步的宏扩展,产生...

abc

. 这再次被重新扫描,但没有进一步的宏扩展要执行,所以这就是最终结果。


推荐阅读