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

ton*_*ark 5 c macros preprocessor

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

int main() {
  printf("The value of A is %dn", A); 
  return 0;
} 
Run Code Online (Sandbox Code Playgroud)

我遇到了上面的代码.我认为在预处理之后,它会被转换为

// code from stdio.h

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

这应该导致编译错误.但是,代码编译良好并产生输出5.

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

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

zwo*_*wol 5

预处理器被定义为在令牌流上运行,而不是文本.你必须通过所有部分的阅读5.1.1,6.46.10 C标准的充分理解它是如何工作的,但关键位是5.1.1.1"翻译的阶段":在第3阶段,源文件"分解为预处理令牌"; 第4,5和6阶段对这些令牌进行操作; 并且在阶段7"每个预处理令牌被转换为令牌".这篇不定冠词至关重要:每个预处理令牌都只是一个令牌.

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

#define A -B
#define B -C
#define C 5
A
Run Code Online (Sandbox Code Playgroud)

然后,在翻译阶段4(宏扩展等)之后,你所拥有的是一系列三个预处理令牌,

<punctuator: -> <punctuator: -> <pp-number: 5>
Run Code Online (Sandbox Code Playgroud)

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

TK_MINUS TK_MINUS TK_INTEGER:5
Run Code Online (Sandbox Code Playgroud)

然后将其解析为表达式-(-(5))而不是as --(5).该标准没有提供任何自由度:一个C编译器解析您的示例--(5)是有缺陷的.

当您要求编译器将预处理的源转储为文本时,标准不会指定该文本的形式; 通常情况下,您获得的内容会根据需要插入空格,以便人类能够像翻译阶段7一样理解它.