预处理在此代码段中如何工作?

How does preprocessing work in this snippet?

#include<stdio.h>
#define A -B
#define B -C
#define C 5

int main() {
  printf("The value of A is %dn", A); 
  return 0;
} 

我看到了上面的代码。我认为经过预处理后,它会转换为

// code from stdio.h

int main() {
  printf("The value of A is %dn", --5); 
  return 0;
}

这应该会导致编译错误。但是,代码可以正常编译并生成输出 5.

在这种情况下如何对代码进行预处理,以免导致编译器错误?

PS:我在 Linux x86-64 上使用 gcc 版本 8.2.0。

预处理器定义为对 令牌 流进行操作,而不是文本。您必须通读 C 标准的所有部分 5.1.1, 6.4, and 6.10 才能完全理解其工作原理,但关键位在 5.1.1.1 "Phases of translation" 中:在阶段 3 中,源文件是 "decomposed into preprocessing tokens";第 4、5 和 6 阶段对这些代币进行操作;在第 7 阶段 "each preprocessing token is converted into a token"。那个不定冠词很关键:每个预处理标记都变成 一个 个标记。

这意味着,如果你从这个源文件开始

#define A -B
#define B -C
#define C 5
A

然后,在翻译阶段 4(宏扩展等)之后,您得到的是三个预处理标记的序列,

<punctuator: -> <punctuator: -> <pp-number: 5>

并且在翻译阶段 7 开始时变为

TK_MINUS TK_MINUS TK_INTEGER:5

然后被解析为表达式 -(-(5)) 而不是 --(5)。该标准在这方面没有提供任何自由度:将您的示例解析为 --(5) 的 C 编译器有缺陷。

当您要求编译器将预处理后的源代码转储为文本时,标准并未指定该文本的格式;通常,您得到的内容会根据需要插入空格,以便人们可以像翻译阶段 7 那样理解它。