以下代码已使用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
<same error>
% g++ -W -Wall -std=c++03
<no errors>
将debug("DEBUG:"__VA_ARGS__);
更改为debug("DEBUG:" __VA_ARGS__);
,即在__VA_ARGS__
之前添加空格,可以使用所有三个-std=
选项进行编译。
你这样做的原因是什么?谢谢。
1条答案
按热度按时间igetnqfo1#
从C11开始支持 * 用户定义的文字量 *,这是一种文字量,包括字符串文字量,后面紧跟一个标识符(没有空格)。一个用户定义的文字量被认为是一个单独的预处理器标记。关于它们的用途的细节,请参见https://en.cppreference.com/w/cpp/language/user_literal。
因此,
"DEBUG:"__VA_ARGS__
是单个预处理器标记,它在宏定义中没有特殊含义。正确的行为是简单地将其原封不动地放入宏扩展中,然后在宏扩展中编译失败,因为没有为__VA_ARG__
后缀声明用户定义的文本运算符。因此GCC将其作为C11代码拒绝是正确的。
这是C11标准草案N3337附录中列出的C03和C11之间向后不兼容的更改之一:https://timsong-cpp.github.io/cppwp/n3337/diff.cpp03.lex
在C11之前,字符串字面量(直到结束的
"
)将是它自己的预处理器标记,后面的标识符是第二个预处理器标记,即使它们之间没有空格。因此GCC在C03模式下接受它也是正确的。(
-std=c++0x
与-std=c++11
相同,C0x是C11起草时的占位符名称)它也与C不兼容(到目前为止的所有版本),因为C也不支持用户定义的文本,并且也将
"DEBUG:"__VA_ARGS__
的两个部分视为两个预处理器令牌。因此,GCC也将其作为C代码接受是正确的(这是
gcc
命令解释.c
文件的方式,而g++
将其视为C)。若要修复此问题,请按照您的建议在
"DEBUG:"
和__VA_ARGS__
之间添加一个空格。这将使其与所有C和C++修订版兼容。