c语言可变长参数传递问题

一、问题描述

C语言中的函数提供了一种可变长参数机制,这个机制使得我们在操作的时候充分自定义自己的功能,例如使用最多的printf函数:

printf("%s: %d", "HelloWorld", 10);

它的函数声明为:

printf(const char *fmt, ...);

其中的...就代表不固定的参数,使用起来十分方便。但是在函数嵌套的时候,不能直接使用...来占位,例如:

#define logerr(s, ...) do { fprintf(stderr, s, ...); } while (0)

编译时就会报错:

va_args.c:4:50: error: expected expression before ‘...’ token
 #define logerr(s, ...) do { fprintf(stderr, "s", ...); } while (0)

如果要嵌套使用,需要通过宏__VA_ARGS__完成:

#define logerr(s, ...) do { fprintf(stderr, s, __VA_ARGS__); } while (0)

二、参数个数为0的问题

使用上面的方法,参数个数为0的时候编译也会报错:

#define logdbg(s, ...) do { fprintf(stderr, s, __VA_ARGS__); } while (0)

int main() {
    logdbg("HelloWorld\n");
    return 0;
}

编译报错:

> gcc va_args.c  -o debug/va_args
va_args.c: In function ‘main’:
va_args.c:5:59: error: expected expression before ‘)’ token
 #define logdbg(s, ...) do { fprintf(stderr, s, __VA_ARGS__); } while (0)

原因是因为参数个数零,预编译后main函数里面的代码变成了:

> gcc -E va_args.c | tail 
int main() {
    const char *msg = "HelloWorld";

    do { fprintf(
# 10 "va_args.c" 3 4
   stderr
# 10 "va_args.c"
   , "HelloWorld\n", ); } while (0);
    return 0;
}

可以看到:fprintf函数的最后是"HelloWorld", );,最后一个逗号和括号之间没有数据,语法不通过。

解决方案

__VA_ARGS__前面加上##,例如:

#include <stdio.h>
#include <stdarg.h>

#define logerr(s, ...) do { fprintf(stderr, s, ##__VA_ARGS__); } while (0)
#define logdbg(s, ...) do { fprintf(stderr, s, ##__VA_ARGS__); } while (0)

int main() {
    const char *msg = "HelloWorld";
    logerr("%s\n", msg);
    logdbg("HelloWorld\n");
    return 0;
}

编译运行:

> make va_args
gcc va_args.c -o debug/va_args
> ./debug/va_args 
HelloWorld
HelloWorld
最后修改:2019 年 06 月 19 日
喜欢就给我点赞吧