可变参数函数
C语言的stdarg.h头文件中定义了一组宏,可用来实现可变参数函数,也称作不定参数函数,这类函数有以下特点:
- 至少有1个固定参数
- 不定参数必须出现在参数的末尾
实现可变参数函数步骤一般如下:
- 定义一个va_list类型的变量,比如ap(argument pointer),用于指向参数;
- 调va_start宏对ap进行初始化:va_start(ap, lastarg),其中lastarg是最后一个固定参数;
- 依次调va_arg宏获取各个不定参数:val = va_arg(ap, type),其中type是当前不定参数的类型;
- 调va_end宏,使ap不再指向堆栈:va_end(ap);
如何判断不定参数是否取完了呢?有两种做法:一种是在不定参数的前面加个固定参数用于描述不定参数,例如printf中的fmt参数,如果不定参数类型都相同,也可以将不定参数的个数作为最后一个固定参数;另一种做法是采用哨兵,将一个无意义的值(例如0/-1/NULL等)作为最后一个不定参数。
下面是一个例子,识别不定参数结尾采用的哨兵做法。
#include <stdio.h>
#include <stdarg.h>
enum {END, STR, DEG, HEX};
void func(char *buf, ...) {
va_list ap;
int type = 0, pos = 0, num;
char *p;
va_start(ap, buf);
type = va_arg(ap, int);
while (END != type) {
if (STR == type) {
p = va_arg(ap, char*);
pos += sprintf(buf + pos, "[%s]", p);
} else if (DEG == type) {
num = va_arg(ap, int);
pos += sprintf(buf + pos, "[%d]", num);
} else if (HEX == type) {
num = va_arg(ap, int);
pos += sprintf(buf + pos, "[0x%X]", num);
}
type = va_arg(ap, int);
}
buf[pos] = 0;
}
int main() {
char buf[128];
func(buf, STR, "seventeen", DEG, 17, HEX, 17, END);
puts(buf);
return 0;
}
运行结果如下:
[seventeen][17][0x11]
用不定参可以方便地封装打印函数,便于调试。
void Log(const char *fmt, ...)
{
va_list ap;
char timebuf[64];
time_t t = time(NULL);
struct tm *local = localtime(&t);
strftime(timebuf, sizeof(timebuf), "%y-%m-%d %H:%M:%S", local);
FILE *fp = fopen("trace.log", "a");
if (fp && ftell(fp) > 1024 * 1024) {
fp = freopen("trace.log", "w", fp);
}
if (!fp) return;
fprintf(fp, "%s ", timebuf);
va_start(ap, fmt);
vfprintf(fp, fmt, ap);
va_end(ap);
fprintf(fp, "\n");
fclose(fp);
}