[C语言]变长函数参数和变长参数宏
C++里对于变化长参数已经有了较好的处理方案,这里对C语言的变长参数进行一个简答的总结。主要分为两块:函数和宏。
函数的变长参数
常见于printf系列函数,以printf的实现为例:
int printf (const char *format, ...) {
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);
return done;
}
主要用到了va_list系列宏,定义在stdarg.h
头文件中。
注意,va_start是把操作指针移动到了变长参数头部,如果需要对变长参数操作多次,需要首先使用va_copy(dst,src)
对变长参数进行备份。
宏的变长参数
宏的变长参数可用于定义一个自定义附加信息的assert:
#define vassert(expr, fmt, ...) \
do { \
if (!(expr)) { \
char *msg; \
asprintf(&msg, fmt, ##__VA_ARGS__); \
__assert_fail(msg, __FILE__, __LINE__, __func__); \
} \
} while (0)
这里用到的是__VA_ARGS__
宏,C99中被标准化,编译时自动替换为实际对应的参数集。
##
的作用是正确处理变长参数为空的情况。
题外话:
这里我们可以额外加入一个宏,使得vassert具有和assert一样的开关性质。
#ifdef NDEBUG
#define vassert(expr, fmt, ...) ((void)0)
#else
#define vassert(expr, fmt, ...) \
...
#endif
使用 #define vassert(expr, fmt, ...) ((void)0)
而非简单的 #define vassert(expr, fmt, ...)
或者 #define vassert(expr, fmt, ...) {}
,是因为这种写法能正常用于逗号表达式,或者条件语句,且能被编译器自动识别进行空语句优化。
// Comma expression
printf("test"), vassert(0, "fail");
// Conditional expression
test-expr ? perror("Hello") : vassert(0, "fail");