C99中的变量宏有一个带空参数的well-known problem.

例子:

#define FOO(...)       printf(__VA_ARGS__)
#define BAR(fmt, ...)  printf(fmt, __VA_ARGS__)

FOO("this works fine");
BAR("this breaks!");

根据C99标准,上述BAR()的使用确实是不正确的,因为它将扩展到:

printf("this breaks!",);

注意后面的逗号-不可行.

一些编译器(例如:VisualStudio2010)会悄悄地为您go 掉后面的逗号.其他编译器(例如:GCC)支持将##置于__VA_ARGS__之前,例如:

#define BAR(fmt, ...)  printf(fmt, ##__VA_ARGS__)

但是,有没有一种符合标准的方法来实现这种行为呢? 也许使用多个宏?

目前,##版本似乎得到了相当好的支持(至少在我的平台上是这样),但我真的更愿意使用符合标准的解决方案.

先发制人:我知道我可以写一个小函数.我正试图用宏来实现这一点.

Edit:下面是一个例子(虽然很简单),说明我为什么要使用BAR()

#define BAR(fmt, ...)  printf(fmt "\n", ##__VA_ARGS__)

BAR("here is a log message");
BAR("here is a log message with a param: %d", 42);

这会自动在我的bar()日志(log)语句中添加一个换行符,假设fmt总是用双引号引起来的C字符串.它不会将换行符作为单独的printf()打印,这在日志(log)记录是行缓冲的并且异步来自多个源的情况下非常有利.

推荐答案

如果您愿意接受一些硬编码的参数数量上限,可以避免使用GCC的,##__VA_ARGS__扩展,如Richard Hansen's answer to this question中所述.如果你不想有任何这样的限制,然而,据我所知,仅使用C99指定的预处理器功能是不可能的;你必须对语言进行一些扩展.clang和icc已经采用了GCC扩展,但MSVC没有.

早在2001年,我就在document N976中编写了用于标准化的GCC扩展(以及相关的扩展,它允许您在rest参数中使用__VA_ARGS__以外的名称),但没有收到委员会的任何回应;我甚至不知道有没有人读过.2016年,它在N2023年再次被提出,我鼓励任何了解该提案的人在 comments 中告诉我们.

C++相关问答推荐

错误:八进制常数中的数字9无效

librsvg rsvg_handle_get_dimensions获取像素大小与浏览器中的渲染大小没有不同

使用SWI—Prolog的qsave_program生成二进制文件有什么好处?'

为什么下面的递归基本情况在C中不起作用?

为什么在C中设置文件的位置并写入文件,填充空字符?

如何将已分配的数组(运行时已知的大小)放入 struct 中?

创建一个fork导致fget无限地重新读取文件

如何将字符串传递给函数并返回在C中更改的相同字符串?

Win32API Wizzard97 PropSheet_SetWizButton不工作

正确的TCP/IP数据包 struct

试图从CSV文件中获取双精度值,但在C++中始终为空

将数据移动到寄存器时出现分段故障

插座打开MacOS组件

S的这种管道实施有什么问题吗?

是否需要包括<;errno.h>;才能使用perror?

GTK函数调用将完全不相关的char* 值搞乱

如何将另一个数组添加到集合中,特别是字符串?

我在C程序的Flex/Bison中遇到语法错误

将非连续物理内存映射到用户空间

在 C/C++ 中原子按位与字节的最佳方法?