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

时间:2018-08-09 17:42:57

标签: 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;
} 

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

// 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。

1 个答案:

答案 0 :(得分:5)

预处理器被定义为对令牌(而非文本)流进行操作。您必须通读C标准的所有5.1.16.46.10部分,以充分了解其工作原理,但关键点在5.1.1.1“翻译阶段”:在阶段3中,源文件被“分解为预处理令牌”;阶段4、5和6在这些令牌上运行;在阶段7中,“每个预处理令牌都转换为令牌”。那篇不确定的文章很关键:每个预处理令牌都变成一个准确的令牌。

这是什么意思,如果您从此源文件开始

#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)。该标准在这方面没有任何余地:C编译器将您的示例解析为--(5)有缺陷。

当您要求编译器将经过预处理的源作为文本转储时,标准未指定该文本的形式。通常情况下,您会获得必要的空格,以使人们能够像翻译阶段7一样理解空格。