尝试了解 C 预处理器

发布于 2024-09-10 13:53:50 字数 918 浏览 3 评论 0原文

为什么这些代码块会产生不同的结果?

一些常见的代码:

#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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

舟遥客 2024-09-17 13:53:50

情况 1 和 2 没有定义的行为,因为您很想将 * 粘贴到一个预处理器标记中。根据预处理器的关联规则,它会尝试将标记 PART1PART2 (或只是 PART2)和 * 粘合在一起。在你的情况下,这可能会默默地失败,这是当事情未定义时可能的结果之一。后跟 * 的标记 PART1PART2 将不再被考虑进行宏扩展。然后字符串化会产生您看到的结果。

我的 gcc 在您的示例中的行为有所不同:

/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 中,您的编译器给出了错误的结果。它应该

  1. 评估参数
    STRINGAFY1
  2. 要做到这一点,它必须扩展 GLUE
  3. GLUE 导致 PART1PART2*
  4. 必须再次扩展,
  5. 结果是 works*
  6. 然后将其传递给
    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 tokens PART1PART2 (or just PART2) and *. In your case this probably fails silently, which is one of the possible outcomes when things are undefined. The token PART1PART2 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:

/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*"

So to summarize your case 1 has two problems.

  • Pasting two tokens that don't result
    in a valid preprocessor token.
  • evaluation order of the ## operator

In case 3, your compiler is giving the wrong result. It should

  1. evaluate the arguments to
    STRINGAFY1
  2. to do that it has to expand GLUE
  3. GLUE results in PART1PART2*
  4. which must be expanded again
  5. the result is works*
  6. which then is passed to
    STRINGAFY1
寻找一个思念的角度 2024-09-17 13:53:50

它完全按照您的指示去做。第一个和第二个获取传入的符号名称并将它们粘贴到一个新符号中。第三个需要 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? :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文