C 可变参数宏无法编译

标签 c gcc macros variadic-macros

我编写的这段代码有一些问题。 GCC 不喜欢它:

#define _DEBUG_ADD(string, ...)                     \
do{                                                 \
if (EVALUATE_TYPE(string)){                         \
size_t  size = strlen(string) + BUFFER_SIZE_DEBUG;  \
char    *buffer = alloca(size);                     \
bzero(buffer, size);                                \
snprintf(buffer, size, string, __VA_ARGS__);        \
fwrite(buffer, strlen(buffer), 1, DEBUG_STREAM); }} \
while(0)

但是 gcc 显示此错误:

../debug.h:33:42: error: expected expression before ')' token
                    snprintf(buffer, size, string, __VA_ARGS__);

我阅读了关于 variadic macro 的 gcc 文档我没有做错。

有人可以指出我的错误吗?我完全迷失了。

编辑:

我就是这么用的

_DEBUG_ADD("Bbox found @ %f %f %f %f", box[0], box[1], box[2], box[3]);

最佳答案

如果我获取您显示的代码片段并填充缺失的位以获得完整的、可编译的源文件,我不会收到任何错误:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define _DEBUG_ADD(string, ...)                     \
do{                                                 \
if (EVALUATE_TYPE(string)){                         \
size_t  size = strlen(string) + BUFFER_SIZE_DEBUG;  \
char    *buffer = alloca(size);                     \
bzero(buffer, size);                                \
snprintf(buffer, size, string, __VA_ARGS__);        \
fwrite(buffer, strlen(buffer), 1, DEBUG_STREAM); }} \
while(0)

#define EVALUATE_TYPE(s) 1
#define BUFFER_SIZE_DEBUG 128
#define DEBUG_STREAM stderr

void test(double box[4])
{
  _DEBUG_ADD("Bbox found @ %f %f %f %f", box[0], box[1], box[2], box[3]);
}

-->

$ gcc -fsyntax-only -Wall test.c
$

这就是为什么我们对minimal, complete, verifiable examples如此大惊小怪的原因。我们不想浪费大量时间找错树。

但是,在这种情况下,我强烈怀疑您的问题实际上是由像this这样的代码触发的:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define _DEBUG_ADD(string, ...)                     \
do{                                                 \
if (EVALUATE_TYPE(string)){                         \
size_t  size = strlen(string) + BUFFER_SIZE_DEBUG;  \
char    *buffer = alloca(size);                     \
bzero(buffer, size);                                \
snprintf(buffer, size, string, __VA_ARGS__);        \
fwrite(buffer, strlen(buffer), 1, DEBUG_STREAM); }} \
while(0)

#define EVALUATE_TYPE(s) 1
#define BUFFER_SIZE_DEBUG 128
#define DEBUG_STREAM stderr

void test(void)
{
  _DEBUG_ADD("got here 1");
}

它产生几乎与您显示的错误消息相同的错误消息:

$ gcc -fsyntax-only -Wall test.c
test.c: In function ‘test’:
test.c:11:43: error: expected expression before ‘)’ token
 snprintf(buffer, size, string, __VA_ARGS__);        \
                                           ^
test.c:21:3: note: in expansion of macro ‘_DEBUG_ADD’
   _DEBUG_ADD("got here 1");
   ^~~~~~~~~~

当您在格式字符串后不给 _DEBUG_ADD 任何参数时,__VA_ARGS__ 会扩展为空,因此“编译器本身”会看到

snprintf(buffer, size, string, );

这确实是一个语法错误。这就是 GNU comma-deletion extension用于:如果将 ## 放在 ,__VA_ARGS__ 之间,则当 __VA_ARGS__ 扩展为时,逗号将被删除什么都没有。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define _DEBUG_ADD(string, ...)                     \
do{                                                 \
if (EVALUATE_TYPE(string)){                         \
size_t  size = strlen(string) + BUFFER_SIZE_DEBUG;  \
char    *buffer = alloca(size);                     \
bzero(buffer, size);                                \
snprintf(buffer, size, string, ##__VA_ARGS__);      \
fwrite(buffer, strlen(buffer), 1, DEBUG_STREAM); }} \
while(0)

#define EVALUATE_TYPE(s) 1
#define BUFFER_SIZE_DEBUG 128
#define DEBUG_STREAM stderr

void test(void)
{
  _DEBUG_ADD("got here 1");
}

-->

$ gcc -fsyntax-only -Wall test.c
$

不幸的是,此扩展仅在 GCC 和 Clang 中可用。我知道 C 和 C++ 委员会正在讨论添加一个可比较但不兼容的功能 Real Soon Now(请参阅 this question 及其答案的评论,以及 C 委员会文件 N2023N2153 ),但即使他们这样做了,可能需要十年左右的时间才能普及到可以使用的程度。

顺便说一句,名称_DEBUG_ADD以下划线开头。所有以下划线开头的名称都保留供 C 编译器和库在至少某些上下文中内部使用。除非您对该语言有更多的经验,否则您不应该为代码中的任何内容指定以下划线开头的名称。 (使用名称以下划线开头的内容是可以的,例如__VA_ARGS___IONBF,但前提是它们已被记录。)

关于C 可变参数宏无法编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46268020/

相关文章:

c - 如何将 gcc 优化级别传递给 cmake 目标?

gcc - 从 OS X/Linux 为 Windows/RPi 交叉编译 Java JNI 库

c++ - 在 OS X 上链接期间包装符号

c++ - c++ : #define MBGL_DEFINE_ENUM(T, 值中的省略号是什么...)?

c++ - 通过 CreateProcessW 使用 "mkdir"创建的目录名称中的垃圾?

c - 使变量成为 const 或 final 是否节省字节或内存?

c++ - lw 在 C 或 C++ 中是如何表示的?

c++ - clang-format:如何对齐结构初始化列表

macros - Julia 宏中的无效赋值错误

c++ - 是否有更简洁的方法来包含基于宏定义部分名称的文件?