6

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

一些常用代码:

#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 单个 '#' 大小写 atm)-删除所有 ## 运算符

第 2 步: - 获取结果字符串并解析它以获取任何宏

现在,我相信所有 3 种情况都应该产生完全相同的结果字符串:

第 1 部分第 2 部分*

因此在第 2 步之后,应该会导致

作品*

但至少应该导致同样的事情。

4

2 回答 2

3

案例 1 和 2 没有定义的行为,因为您很想将 a 粘贴*到一个预处理器令牌中。根据您的预处理器的关联规则,这要么尝试将标记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
于 2010-07-19T08:09:31.153 回答
1

它正在做你告诉它做的事情。第一个和第二个获取传入的符号名称并将它们一起粘贴到一个新符号中。第三个需要 2 个符号并粘贴它们,然后您自己将 * 放入字符串中(最终将评估为其他内容。)

结果的问题究竟是什么?你期望得到什么?这一切似乎都像我期望的那样工作。

那么问题当然是你为什么要像这样玩弄象征符号的黑暗艺术呢?:)

于 2010-07-19T07:17:25.070 回答