g++ C++11模式下__VA_ARGS__宏邻接字符串报错原因问询
编译行为差异问题说明
以下代码使用gcc-5.4.0编译时无任何问题:
% gcc -W -Wall a.c ...
#include <stdio.h> #include <stdarg.h> static int debug_flag; static void debug(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } #define DEBUG(...) \ do { \ if (debug_flag) { \ debug("DEBUG:"__VA_ARGS__); \ } \ } while(0) int main(void) { int dummy = 10; debug_flag = 1; DEBUG("debug msg dummy=%d\n", dummy); return 0; }
但使用g++编译该代码时会出现特殊现象:
% g++ -W -Wall -std=c++11 a.c a.c: In function ‘int main()’: a.c:18:10: error: unable to find string literal operator ‘operator""__VA_ARGS__’ with ‘const char [8]’, ‘long unsigned int’ arguments debug("DEBUG: "__VA_ARGS__); \ % g++ -W -Wall -std=c++0x <相同报错> % g++ -W -Wall -std=c++03 <无报错>
若将debug("DEBUG:"__VA_ARGS__);修改为debug("DEBUG:" __VA_ARGS__);,即在__VA_ARGS__前添加空格,三种-std=编译参数下均可正常编译通过。
差异产生的根本原因
这个问题的核心触发点是C++11新增的**用户自定义字面量(UDL)**语法规则,具体逻辑如下:
- C11(含其早期草稿版本C0x)新增了用户自定义字面量特性,语法上明确规定:如果字符串/数字字面量和后面紧跟的标识符之间没有任何空白、标点类分隔符,编译器会将二者整体识别为一个用户自定义字面量调用,也就是去找名为
operator"" 标识符的重载函数完成字面量解析,不会将二者判定为两个独立的语法token。 - 代码中编写的
"DEBUG:"__VA_ARGS__刚好命中这个规则:在C++11及以上标准的词法分析阶段,编译器会直接把__VA_ARGS__识别为"DEBUG:"这个字符串字面量的UDL后缀,根本不会先触发可变参数宏的展开逻辑,自然会因为找不到operator""__VA_ARGS__这个字面量运算符抛出编译错误。 - 其他编译场景不报错的原因非常直接:
- 用gcc编译C代码时,C语言标准从未引入用户自定义字面量语法,相邻字符串字面量自动拼接是C语言原生支持的规则,即使两个字符串中间没有空格,也会被判定为独立token等待后续拼接,不会触发UDL识别逻辑。
- 用g指定
-std=c++03编译时,C03标准发布早于C++11,完全没有UDL相关的语法规则,编译器会按照传统词法规则把"DEBUG:"和__VA_ARGS__识别为两个独立token,正常展开宏之后完成字符串拼接,不会报错。
- 加空格就能修复的逻辑也很简单:空白符是词法分析阶段明确的token分隔符,加了空格之后,编译器不会再把
"DEBUG:"和__VA_ARGS__识别为一个整体的UDL结构,会按照正常流程展开可变参数宏,展开后得到两个相邻的字符串字面量,自动拼接后完全符合语法要求,因此所有编译模式下都能正常通过。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

