stdarg, va_start, va_arg, va_end, va_copy可变参数列表。
#include
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
一个函数可能有不同数量的不同类型的参数。头文件
va_start()
va_start()宏初始化一个被后面va_arg()和va_end()使用的ap,并且必须率先调用。last是在可变参数列表之前的最后一个我们知道类型的参数的名字。因为va_start()宏可能会用到last的地址,所以不要把last声明成寄存器变量或者函数、数组类型。
va_arg()
va_arg()这个宏扩展到调用中的下一个参数的值和类型。ap就是由va_start()初始化的va_list类型的ap。每次调用va_arg(),它都会修改ap,使它的下一次调用返回下一个参数。参数type就是一个类型名,这样就可以获取一个类型对象了。
在调用va_start()之后第一次调用va_arg()它就返回last后面的第一个参数。后面的调用,依次返回剩下的参数的值。
如果后面没有其它参数了,或者type和实际的下一个参数不匹配(会按照默认参数扩展?),就会产生随机的错误。
如果ap在调用va_arg(ap, type)的时候是指向一个函数的,那么在这个函数返回的时候ap的值是未定义的。
va_end()
每次在一个函数里面调用va_start(),必须有一个va_end()与它进行匹配。用多对va_start()和va_end()多次遍历一个va_list也是可以的。va_end()可以是一个宏或者一个函数。
va_copy()
va_copy()将之前初始化的可变参数列表从src拷贝到dest。这个行为等价于先对dest做va_start(),然后调用获得src相同次数的va_arg()。
一个显而易见的实现是让va_list指向可变函数的栈帧。这样的话(目前最常见),这样的话,赋值操作好像也没什么问题。
va_list aq = ap;
不幸的是,有些系统也把va_list实现成一个指针数组。那这样的话就需要:
va_list aq;
*aq = *ap;
最后,对于参数传递到寄存器的系统,就有必要用va_start()分配内存,存储参数,并且表明哪个参数是下一个。这样的话va_arg()才可以对参数进行遍历。然后va_end()可以释放内存。为了解决这个问题,C99添加了一个宏va_copy()。那么上面的赋值就可以用下面的函数代替:
va_list aq;
va_copy(aq, ap);
...
va_end(aq);
每次调用va_copy(),必须在同一个函数里面调用对应的va_end()。有些系统不提供va_copy(),但是有__va_copy,这个是草案里面使用的名字。
这部分使用的术语的解释可以参看attribute(7)。
接口 | 属性 | 值 |
va_start(), va_end(), va_copy() | 线程安全性 | MT-safe |
va_arg() | 线程安全性 | MT-safe race:ap |
va_start(), va_arg(), va_end()宏遵守C89,va_copy()遵守C99。
这些宏和它们代替的历史上的宏并不兼容。一个后向兼容的版本可以在头文件
历史的背景是:
#include
void
foo(va_alist)
va_dcl
{
va_list ap;
va_start(ap);
while (...) {
...
x = va_arg(ap, type);
...
}
va_end(ap);
}
有些系统,va_end包含一个“}”,va_start包含一个“{”,这样的话两个宏就必须出现在同一个函数里面,否则编译就不会通过。
不像varargs宏,stdarg宏不允许程序员编码一个没有固定参数的函数。这个问题主要是在将varargs代码转换为stdarg代码的时候引入了很多工作量。当然,这个也给从可变参数函数内部将它所有的参数传递给一个有一个va_list参数的函数(比如vfprintf(3))带来了很多的困难。
函数foo把一个格式字符串作为参数,并且根据每个格式字符表明的类型打印出每个参数。
#include
#include
void
foo(char *fmt, ...)
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while(*fmt)
switch(*fmt++) {
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* Need a cast hear since va_arg only
takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
本页是Linux man-page项目4.15版的一部分。关于项目的描述,报bug相关信息,以及本页的最新版本可以在https://www.kernel.org/doc/man-pages/找到。