考虑这个场景:
$ 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有一个由-Wall
启用的选项-Wmultistatement-macros
,用于在宏扩展到多个语句时发出警告,这些语句并非都由同一if
、while
等保护。请参阅手册。手册示例的填写版本:
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)
| ^~
但CCD_ 10和CCD_。
显然,当您一起运行预处理和编译过程时,它们之间共享了一些信息,因此编译器阶段知道foo(); bar()
是由宏的扩展产生的。但gcc -E
发出的经过预处理的源文件本身并不包含这些信息,因此当gcc
在foo.i
上运行时,它不知道原来有一个宏,因此无法发出警告。
根据语言律师标签,我认为这种行为没有一致性问题。C标准只是粗略地说,在某些情况下,实现必须发布诊断,否则必须在实现限制内翻译符合要求的程序。除此之外,只要诊断对符合要求的程序不是致命的,它就可以随时免费发布诊断。没有规定是否发布诊断或应该说什么的决定应该完全由预处理后翻译单元的内容决定。编译器当然可以在预处理之前考虑源的内容,或其他不相关的翻译单元的内容(例如,检测全局声明何时不匹配(,或系统上其他文件的内容(如编译器配置文件(,或月球的当前相位。没有反对它的规则。我无法想象标准作者会想要禁止像-Wmultistatement-macros
这样有用的功能。
是的,这总是事实。编译器编译经过预处理的源代码,以便在这两种情况下都获得相同的源代码。