c - 替换列表中涉及## 运算符的参数化宏
问题描述
在我正在阅读的“ 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))
,因此它仍然会失败,就像我们提出的原始案例一样。
任何帮助是极大的赞赏!
解决方案
当预处理器在扫描源代码行时检测到类似函数的宏调用时,它会在将宏的参数替换为宏的替换文本之前完全展开宏的参数,除非参数显示为字符串化 ( #
) 或标记粘贴 ( ##
) 运算符,其文字值用于操作。然后重新扫描生成的替换文本,其中包含扩展的参数以及替换的任何#
和##
操作的结果,以查找要扩展的其他宏。
因此,与...
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
. 这再次被重新扫描,但没有进一步的宏扩展要执行,所以这就是最终结果。
推荐阅读
- gun - 处理连接/断开状态并在 GUN 中获取在线用户
- node.js - 在 linux 上使用 clasp 时 node-v57-linux-x64-glibc/grpc_node.node 丢失
- python - 从网页抓取时如何识别要指定的 HTML 标签或类?
- batch-file - 通过批处理文件识别 Windows 上的版本
- php - 从下拉菜单中获取输入到 php
- javascript - 从导航栏过滤反应组件
- maven - 停止记录 Gatling 请求
- reactjs - 在应用程序中使用 shopify api 创建客户
- android - TabLayout 选定的文本颜色同时显示在两个选项卡中
- datetime - 如何在时区为 IST 的 presto 中将纪元时间转换为人类可读的日期