了解C中的宏

Anw*_*ain 7 c c++ c-preprocessor

为什么以下代码的输出值为5?

#include<stdio.h>

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

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

chq*_*lie 8

这是一个棘手的问题,因为它是编译器预处理器的压力测试.

取决于预处理器是编译器的集成阶段还是通过文件或管道将其输出传递给编译器的单独程序,在这种情况下是否足够小心不执行错误的令牌粘贴,您可能会得到预期的输出:5或者您可能会收到编译错误.

在预处理内容之后stdio.h,源代码扩展为:

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

但是这两个-是独立的标记,因此,如果预处理器在输出中将它们分开,你可能会得到一个输出的程序5或一个不能编译的程序,因为--它不能应用于文字5.

无论是gccclang预处理器的行为正确和分离-一个额外的空间,以防止标记粘贴时,他们产生与预处理程序输出-E命令行选项.在扩展后,它们将此输出为预处理源代码<stdio.h>:

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

尝试使用您自己的编译器来检查它是如何扩展源代码的.似乎Visual Studio 2013和2015未通过测试并拒绝该程序并出现错误.

为了清楚起见,我没有说程序的行为应该依赖于编译器架构.我希望至少有一个常见的C编译器会错误地处理这种一致性测试.我并不感到惊讶MS Visual Studio 2013和2015测试失败.

仅在预处理器的文本输出中需要额外空间.如果Visual Studio使用多个单独的阶段并不重要,源程序是完全有效的,并且它们无法编译它是一个BUG.

  • @axiac,当预处理器以文本形式发出结果时,合成空间,而不是直接传递给编译器的标记序列.这允许它确保如果输出被重新读作C源,它将表示与原始源相同的令牌序列.这是一个实施考虑的质量,因为标准没有说明这种转换回文本. (4认同)

Jea*_*bre 5

无需编译此代码,只需gcc -E在其上使用(预处理器)并查看会发生什么:

<lots of output expanding stdio.h> ...

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

显然结果是5(可以通过查看嵌套宏来猜测,但是一个小的预处理器测试不会受到伤害).

(其他答案指出,一些编译器可能会处理减号的预处理,这会导致编译器错误.gcc处理得很好.)