编译先前未预处理的源代码是否会导致与编译先前预处理的源代码相同的诊断?

Should compilation of previously non-preprocessed source code lead to the same diagnostics as compilation of previously preprocessed source code?

考虑这种情况:

$ cat t783.c
#define EXPR ("xxx" + 1)
char* s = EXPR;

$ clang t783.c -c
t783.c:2:11: warning: adding 'int' to a string does not append to the string [-Wstring-plus-int]

$ clang t783.c -E | clang -xc - -c
t783.c:2:18: warning: adding 'int' to a string does not append to the string [-Wstring-plus-int]

在这里我们看到编译之前未预处理的源代码(案例 1)导致与编译之前预处理的源代码(案例 2)相同的诊断。

问题:这总是正确的吗? (当然,不包括与预处理器本身相关的诊断。)

换句话说:诊断是否“预处理器不敏感”?

原因:更好地理解编译器。

是的,这永远是事实。编译器编译预处理的源代码,因此在两种情况下它将获得相同的源代码。

不,不总是。

例如,GCC 有一个选项 -Wmultistatement-macros,由 -Wall 启用,当宏扩展到多个语句时发出警告,这些语句并非全部由相同的 if 保护,while,等等 See manual。手册示例的填充版本:

void foo(void);
void bar(void);

#define BLAH foo(); bar()

void qux(int cond) {
    if (cond)
        BLAH;
}

程序员可能打算仅在 cond 为真时调用 foo()bar(),但实际上 bar() 将始终被调用。

运行 gcc -Wall -c foo.c 产生警告:

foo.c: In function ‘qux’:
foo.c:4:14: warning: macro expands to multiple statements [-Wmultistatement-macros]
    4 | #define BLAH foo(); bar()
      |              ^~~
foo.c:8:2: note: in expansion of macro ‘BLAH’
    8 |  BLAH;
      |  ^~~~
foo.c:7:5: note: some parts of macro expansion are not guarded by this ‘if’ clause
    7 |     if (cond)
      |     ^~

但是 gcc -E -Wall foo.c > foo.igcc -Wall -c foo.i 分别根本不产生任何诊断。

显然,当您 运行 将它们放在一起时,预处理和编译过程之间会共享一些信息,因此编译器阶段知道 foo(); bar() 是宏扩展的结果。但是gcc -E发出的预处理源文件本身并不包含这些信息,所以当gccfoo.i上是运行时,它不知道原来有一个宏, 所以它不能给出警告。


根据 标记,我认为此行为没有一致性问题。 C 标准仅粗略地说,实现必须在某些情况下发出诊断,否则必须在实现限制内转换符合要求的程序。除此之外,它可以随时发布诊断信息,只要它们对符合要求的程序来说不是致命的。没有规则决定是否发出诊断,或者应该说什么,应该完全由预处理后的翻译单元的内容决定。编译器当然可以在预处理之前考虑源代码的内容,或者其他不相关的翻译单元的内容(例如检测全局声明何时不匹配),或者系统上其他文件的内容(例如编译器配置文件) ),或当前的月相。没有规则反对它。而且我无法想象标准作者会想要禁止像 -Wmultistatement-macros.

这样有用的功能