STDARG - Linux手册页
Linux程序员手册 第3部分
更新日期: 2020-06-09
名称
stdarg,va_start,va_arg,va_end,va_copy-变量参数列表
语法
#包括
无效va_start(va_list ap,last);
输入va_arg(va_list ap,type);
无效va_end(va_list ap);
无效va_copy(va_list dest,va_list src);
说明
可以使用数量众多,类型不同的参数来调用函数。包含文件声明了一个va_list类型,并定义了三个宏,用于逐步通过一个参数列表,这些参数的数量和类型对于调用的函数是未知的。
所调用的函数必须声明一个va_list类型的对象,该对象由宏va_start(),va_arg()和va_end()使用。
va_start()
va_start()宏会初始化ap,以供va_arg()和va_end()后续使用,并且必须首先调用。
参数last是变量参数列表之前的最后一个参数的名称,即调用函数知道其类型的最后一个参数。
由于此参数的地址可以在va_start()宏中使用,因此不应将其声明为寄存器变量,函数或数组类型。
va_arg()
va_arg()宏扩展为具有调用中下一个参数的类型和值的表达式。参数ap是由va_start()初始化的va_list ap。每次调用va_arg()都会修改ap,以便下一次调用返回下一个参数。参数类型是指定的类型名称,因此只需在类型上添加*即可获得具有指定类型的对象的指针的类型。
在va_start()宏之后的第一次使用va_arg()宏将返回最后一个参数。连续调用将返回其余参数的值。
如果没有下一个自变量,或者类型与实际的下一个自变量的类型不兼容(根据默认自变量提升),则将发生随机错误。
如果将ap传递给使用va_arg(ap,type)的函数,则在该函数返回后ap的值是不确定的。
va_end()
va_start()的每次调用必须与同一函数中va_end()的相应调用相匹配。调用va_end(ap)之后,变量ap未定义。可以多次遍历列表,每个遍历都由va_start()和va_end()括起来。 va_end()可以是宏或函数。
va_copy()
va_copy()宏将(先前初始化的)变量参数列表src复制到dest。行为就像是使用相同的最后一个参数将va_start()应用于dest,然后是用于达到src当前状态的va_arg()调用次数相同。
一个明显的实现是让va_list是指向可变参数函数的堆栈框架的指针。在这样的设置中(到目前为止是最常见的),似乎没有什么反对任务的
va_list aq = ap;
不幸的是,也有一些系统使它成为指针数组(长度为1),并且有一个需求
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)。
Interface | Attribute | Value |
va_start(),va_end(),va_copy() | Thread safety | MT-Safe |
va_arg() | Thread safety | MT-Safe race:ap |
遵循规范
va_start(),va_arg()和va_end()宏符合C89。 C99定义了va_copy()宏。
BUGS
与历史上的varargs宏不同,stdarg宏不允许程序员编写没有固定参数的函数。此问题主要在将varargs代码转换为stdarg代码时产生,但对于希望将其所有参数传递给带有va_list参数的函数(例如vfprintf(3))的可变参数函数,也带来了困难。
示例
函数foo接受一串格式字符,并根据类型打印出与每个格式字符关联的参数。
#include <stdio.h> #include <stdarg.h> void foo(char *fmt, ...) /* '...' is C syntax for a variadic function */ { va_list ap; int d; char c, *s; va_start(ap, fmt); while (*fmt) switch (*fmt++) { case aqsaq: /* string */ s = va_arg(ap, char *); printf("string %s\n", s); break; case aqdaq: /* int */ d = va_arg(ap, int); printf("int %d\n", d); break; case aqcaq: /* char */ /* need a cast here since va_arg only takes fully promoted types */ c = (char) va_arg(ap, int); printf("char %c\n", c); break; } va_end(ap); }
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。