我编写的这段代码有一些问题。 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 委员会文件 N2023 和 N2153 ),但即使他们这样做了,可能需要十年左右的时间才能普及到可以使用的程度。
顺便说一句,名称_DEBUG_ADD
以下划线开头。所有以下划线开头的名称都保留供 C 编译器和库在至少某些上下文中内部使用。除非您对该语言有更多的经验,否则您不应该为代码中的任何内容指定以下划线开头的名称。 (使用名称以下划线开头的内容是可以的,例如__VA_ARGS__
和_IONBF
,但前提是它们已被记录。)
关于C 可变参数宏无法编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46268020/