c++ - 试图了解 C 预处理器

标签 c++ c c-preprocessor stringification

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

一些常用代码:

#define PART1PART2 works
#define STRINGAFY0(s) #s
#define STRINGAFY1(s) STRINGAFY0(s)

案例一:

#define GLUE(a,b,c) a##b##c  
STRINGAFY1(GLUE(PART1,PART2,*))
//yields
"PART1PART2*"

案例二:

#define GLUE(a,b) a##b##*
STRINGAFY1(GLUE(PART1,PART2))
//yields
"works*"

案例三:

#define GLUE(a,b) a##b
STRINGAFY1(GLUE(PART1,PART2*))
//yields
"PART1PART2*"

我正在使用来自 VS.net 2005 sp1 的 MSVC++

编辑: 目前我认为预处理器在扩展宏时是这样工作的: 步骤1: - 带上尸体 - 删除## 运算符周围的任何空格 - 如果找到与参数名称匹配的标识符,则解析字符串: - 如果它在## 运算符旁边,则将标识符替换为参数的字面值(即传入的字符串) - 如果它不在## 运算符旁边,则首先对参数值运行整个解释过程,然后用该结果替换标识符。 (忽略 stringafy 单个 '#' 大小写 atm) -删除所有##运算符

第 2 步: - 获取结果字符串并将其解析为任何宏

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

第一部分第二部分*

因此在第 2 步之后,结果应该是

作品*

但至少应该产生相同的结果。

最佳答案

情况 1 和情况 2 没有定义的行为,因为您很想将 * 粘贴到一个预处理器 token 中。根据预处理器的关联规则,这会尝试将标记 PART1PART2(或只是 PART2)和 * 粘合在一起。在您的情况下,这可能会无声地失败,这是未定义的可能结果之一。标记 PART1PART2 后跟 * 将不再被考虑用于宏扩展。 Stringfication 然后生成您看到的结果。

我的 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 有两个问题。

  • 粘贴两个没有结果的标记 在有效的预处理器 token 中。
  • ## 运算符的求值顺序

在情况 3 中,您的编译器给出了错误的结果。应该

  1. 评估论点 STRINGAFY1
  2. 要做到这一点,它必须扩展 GLUE
  3. GLUE 结果为 PART1PART2*
  4. 必须再次展开
  5. 结果是有效*
  6. 然后传递给 STRINGAFY1

关于c++ - 试图了解 C 预处理器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3279014/

相关文章:

c - 为什么鼓励使用 "declared data types"而不鼓励使用 "processor-dependent data types"?

c++ - 如果没有线程阻塞信号,并且一个线程在 `sigwait()` 中,是否可以保证被阻塞的线程将接收到下一个信号?

c - 将 Homebrew 编译的 openmpi(或 mpich2)链接到 Homebrew 的 gcc

c - 使用宏在 C 中实现通用 vector 。这是个好主意吗?

C编程手动递增信号处理程序的PC

c++ - 递归函数核心转储

gcc - 自定义 gcc 预处理器

c++ - 我不明白为什么 for-loop 不适用于此代码

c++ - 如何在Qt中将json数据附加到现有的json文件中

c++ - 来自字符串的 GUID