gcc c++抱怨__VA_ARGS__

kq4fsx7k  于 2023-03-12  发布在  其他
关注(0)|答案(1)|浏览(242)

以下代码已使用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=选项进行编译。
你这样做的原因是什么?谢谢。

igetnqfo

igetnqfo1#

从C11开始支持 * 用户定义的文字量 *,这是一种文字量,包括字符串文字量,后面紧跟一个标识符(没有空格)。一个用户定义的文字量被认为是一个单独的预处理器标记。关于它们的用途的细节,请参见https://en.cppreference.com/w/cpp/language/user_literal
因此,"DEBUG:"__VA_ARGS__是单个预处理器标记,它在宏定义中没有特殊含义。正确的行为是简单地将其原封不动地放入宏扩展中,然后在宏扩展中编译失败,因为没有为__VA_ARG__后缀声明用户定义的文本运算符。
因此GCC将其作为C
11代码拒绝是正确的。
这是C11标准草案N3337附录中列出的C03和C11之间向后不兼容的更改之一:https://timsong-cpp.github.io/cppwp/n3337/diff.cpp03.lex
在C
11之前,字符串字面量(直到结束的")将是它自己的预处理器标记,后面的标识符是第二个预处理器标记,即使它们之间没有空格。
因此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++修订版兼容。

相关问题