GCC中-Wc++-compat对未展开宏内标识符的警告是否属于误报?
最近我在给C代码开启一些非默认警告编译时,发现GCC的-Wc++-compat选项可能存在误报——它居然对未展开的宏定义里的标识符发出了C++兼容性警告。先给你看看这段触发问题的代码:
触发问题的代码
main.c
#include <stdio.h> #define X_KEYWORDS \ X(bool) \ X(not) \ X(and) \ X(or) \ X(xor) \ X(nullptr) \ X(true) \ X(false) int main(void) { # define X(keyword) puts(#keyword); X_KEYWORDS # undef X return 0; }
编译警告情况
当用-Wc++-compat选项编译时,GCC会输出这些警告:
$ gcc main.c -Wc++-compat main.c:5:11: warning: identifier "not" is a special operator name in C++ [-Wc++-compat] 5 | X(not) \ | ^ main.c:6:11: warning: identifier "and" is a special operator name in C++ [-Wc++-compat] 6 | X(and) \ | ^ main.c:7:11: warning: identifier "or" is a special operator name in C++ [-Wc++-compat] 7 | X(or) \ | ^ main.c:8:11: warning: identifier "xor" is a special operator name in C++ [-Wc++-compat] 8 | X(xor) \ |
问题分析
你看,GCC错误地在宏定义阶段就对not、and这些标识符发出了警告,但实际上这些宏还没被展开。哪怕我把main函数写成空的int main(void) {},这些警告照样会出现。更关键的是,就算宏展开后,代码是通过#keyword把标识符转成了字符串字面量,这在C++里完全是合法的——字符串里的这些词根本不是运算符,不会有任何兼容性问题。
接下来回答你提出的三个问题:
1. 我能不能直接认定这是GCC的专属bug?
完全可以,这确实是GCC的问题。你看Clang用同样的-Wc++-compat选项编译这段代码,就完全没有警告。而且从逻辑上来说,-Wc++-compat应该检查的是实际生成的代码是否有C++兼容性问题,而不是还没展开的宏定义内容。
2. 有没有可能这些警告其实是合理的?
我想不出任何合理的场景。这些警告针对的是宏定义里的内容,而非最终编译的代码。就算你把这段代码当成C来编译,它也是完全合法的——展开后只是输出几个字符串,C编译器根本不会把字符串里的内容当成运算符。所以这些警告纯粹是误报。
3. 为什么它只警告<iso646.h>里的那些标识符,而不是bool、nullptr这些C++关键字?
这应该是GCC实现-Wc++-compat选项时的细节问题。它对C++关键字(比如bool、nullptr)的检查逻辑和对<iso646.h>里的替代运算符名的检查逻辑不一样:前者可能只在宏展开后的代码中进行检查,而后者错误地在宏定义阶段就触发了检查。
补充一下,这个问题已经被提交到GCC的官方bug追踪系统中,后续应该会被修复。
备注:内容来源于stack exchange,提问作者sleeptightAnsiC

