尝试了解 C 预处理器
为什么这些代码块会产生不同的结果?
一些常见的代码:
#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++
编辑: 目前我认为预处理器在扩展宏时是这样工作的: 步骤一: - 带走尸体 - 删除 ## 运算符周围的所有空格 - 如果找到与参数名称匹配的标识符,则解析字符串: -如果它位于##运算符旁边,则将标识符替换为参数的文字值(即传入的字符串) -如果它不在 ## 运算符旁边,则首先对参数的值运行整个解释过程,然后用该结果替换标识符。 (忽略 atm 中的字符串“#”) -删除所有##运算符
第2步: - 现在获取该结果字符串并解析它以查找任何宏
,从此我相信所有 3 种情况都应该产生完全相同的结果字符串:
PART1PART2*
,因此在步骤 2 之后,应该会产生
作品*
,但至少应该产生同样的事情。
Why do these blocks of code yield different results?
Some common code:
#define PART1PART2 works
#define STRINGAFY0(s) #s
#define STRINGAFY1(s) STRINGAFY0(s)
case 1:
#define GLUE(a,b,c) a##b##c
STRINGAFY1(GLUE(PART1,PART2,*))
//yields
"PART1PART2*"
case 2:
#define GLUE(a,b) a##b##*
STRINGAFY1(GLUE(PART1,PART2))
//yields
"works*"
case 3:
#define GLUE(a,b) a##b
STRINGAFY1(GLUE(PART1,PART2*))
//yields
"PART1PART2*"
I am using MSVC++ from VS.net 2005 sp1
Edit:
it is currently my belief that the preprocessor works like this when expanding macros:
Step 1:
- take the body
- remove any whitespace around ## operators
- parse the string, in the case that an identifier is found that matches the name of a parameter:
-if it is next to a ## operator, replace the identifier with the literal value of the parameter (i.e. the string passed in)
-if it is NOT next to a ## operator, run this whole explanation process on the value of the parameter first, then replace the identifier with that result.
(ignoring the stringafy single '#' case atm)
-remove all ## operators
Step 2:
- take that resultant string and parse it for any macros
now, from that I believe that all 3 cases should produce the exact same resultant string:
PART1PART2*
and hence after step 2, should result in
works*
but at very least should result in the same thing.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
情况 1 和 2 没有定义的行为,因为您很想将
*
粘贴到一个预处理器标记中。根据预处理器的关联规则,它会尝试将标记PART1PART2
(或只是PART2
)和*
粘合在一起。在你的情况下,这可能会默默地失败,这是当事情未定义时可能的结果之一。后跟*
的标记PART1PART2
将不再被考虑进行宏扩展。然后字符串化会产生您看到的结果。我的 gcc 在您的示例中的行为有所不同:
所以总结一下您的案例 1 有两个问题。
在有效的预处理器标记中。
##
运算符的求值顺序在情况 3 中,您的编译器给出了错误的结果。它应该
STRINGAFY1
GLUE
GLUE
导致PART1PART2*
works*
STRINGAFY1
cases 1 and 2 have no defined behavior since your are tempting to paste a
*
into one preprocessor token. According to the association rules of your preprocessor this either tries to glue together the tokensPART1PART2
(or justPART2
) and*
. In your case this probably fails silently, which is one of the possible outcomes when things are undefined. The tokenPART1PART2
followed by*
will then not be considered for macro expansion again. Stringfication then produces the result you see.My gcc behaves differently on your examples:
So to summarize your case 1 has two problems.
in a valid preprocessor token.
##
operatorIn case 3, your compiler is giving the wrong result. It should
STRINGAFY1
GLUE
GLUE
results inPART1PART2*
works*
STRINGAFY1
它完全按照您的指示去做。第一个和第二个获取传入的符号名称并将它们粘贴到一个新符号中。第三个需要 2 个符号并粘贴它们,然后您自己将 * 放入字符串中(最终将计算为其他内容。)
结果到底有什么问题?你期望得到什么?一切似乎都按照我的预期进行。
当然,问题是你为什么要玩这种符号修改的黑暗艺术呢? :)
It's doing exactly what you are telling it to do. The first and second take the symbol names passed in and paste them together into a new symbol. The third takes 2 symbols and pastes them, then you are placing the * in the string yourself (which will eventually evaluate into something else.)
What exactly is the question with the results? What did you expect to get? It all seems to be working as I would expect it to.
Then of course is the question of why are you playing with the dark arts of symbol munging like this anyways? :)