尝试了解 C 预处理器
-
17-09-2020 - |
题
为什么这些代码块会产生不同的结果?
一些常用代码:
#define PART1PART2 works
#define STRINGAFY0(s) #s
#define STRINGAFY1(s) STRINGAFY0(s)
情况1:
#define GLUE(a,b,c) a##b##c
STRINGAFY1(GLUE(PART1,PART2,*))
//yields
"PART1PART2*"
案例2:
#define GLUE(a,b) a##b##*
STRINGAFY1(GLUE(PART1,PART2))
//yields
"works*"
案例3:
#define GLUE(a,b) a##b
STRINGAFY1(GLUE(PART1,PART2*))
//yields
"PART1PART2*"
我正在使用 VS.net 2005 sp1 中的 MSVC++
编辑:目前我认为预处理器在扩展宏时是这样工作的:步骤1:- 拿起身体 - 删除##运算符周围的所有空格 - 解析字符串,如果发现标识符与参数的名称匹配:-如果它位于 ## 运算符旁边,则将标识符替换为参数的文字值(即字符串传递给) - 如果它不在##运算符旁边,请先在参数的值上运行整个说明过程,然后用该结果替换标识符。(忽略Stringafy单#'case atm) - 删除所有##操作员
第2步:- 获取结果字符串并解析它以查找任何宏
现在,我相信所有 3 种情况都应该产生完全相同的结果字符串:
第 1 部分第 2 部分*
因此在步骤 2 之后,应该会导致
作品*
但至少应该导致同样的结果。
解决方案
情况 1 和 2 没有定义的行为,因为您很想粘贴 *
到一个预处理器标记中。根据预处理器的关联规则,这要么尝试将标记粘合在一起 PART1PART2
(要不就 PART2
) 和 *
. 。在你的情况下,这可能会默默地失败,这是当事情未定义时可能的结果之一。代币 PART1PART2
其次是 *
然后将不再考虑进行宏扩展。然后字符串化会产生您看到的结果。
我的海湾合作委员会在你的例子中表现不同:
/usr/bin/gcc -O0 -g -std=c89 -pedantic -E test-prepro.c
test-prepro.c:16:1: error: pasting "PART1PART2" and "*" does not give a valid preprocessing token
"works*"
总结一下你的案例 1 有两个问题。
- 粘贴两个代币不会导致有效的预处理器令牌。
- 的评估顺序
##
操作员
在情况 3 中,您的编译器给出了错误的结果。它应该
- 评估参数
STRINGAFY1
- 要做到这一点,它必须扩大
GLUE
GLUE
结果是PART1PART2*
- 必须再次扩展
- 结果是
works*
- 然后传递给
STRINGAFY1
其他提示
它正在做什么你告诉它要做的事情。第一个和第二个采取符号名称传递并将它们粘贴到一个新的符号中。第三个是2个符号并粘贴它们,然后你自己把*放在字符串中(最终会评估为其他东西。)
结果是什么问题?你期望得到什么?这一切似乎都在工作,因为我期待它。
然后当然是你为什么和任何像这样的符号令人营地一起玩的问题?:)