|
名称:
stdarg, va_start, va_arg, va_end, va_copy -- 可变参数列表
摘要:
#include <stdarg.h>
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);
描述:
也许有时会需要不同类型、不同数目的参数来调用函数。<stdarg.h> 声明了一个 va_list 类型,还定义了三个宏来逐步跟踪一个参数列表。对于调用函数来说,它并不会知道这个列表里的参数个数及其类型。
调用函数必须声明一个 va_list 类型的变量。va_list 类型在宏 va_start(),va_arg(),和 va_end() 中用到。
va_start()
宏 va_start() 初始化 ap 这个参数,这是为了后面的 va_arg() 和 va_end() 会用到此参数。这个宏是必须被第一个调用的。
last 参数是可变参数列表之前的最近的一个参数的名字,也就是说,这是调用函数 所知道的最近的一个类型。对于 last 的翻译似乎很是晦涩。看下面的具体应用:
void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
.....
}
上面的程序片段中,err_sys() 是我们自定义的一个函数。在 err_sys() 中有参数 fmt,也就是 va_start() 中的 last 。这个参数位于可变参数列表(用 ... 表示)之前。也就是说,err_sys() 函数一开始明确就能知道参数。 由于这个参数( last )的地址可能会在 va_start() 中用到,所以它不能是一个寄存器变量,或者是一个函数,抑或是一个数组类型。
va_arg()
va_arg() 宏展开成一个表达式,这个表达式含有调用函数中下一个参数的值和类型。
其中,ap 是经由 va_start() 初始化后的 va_list ap 。每次调用 va_arg() 都会改变 ap,这样一来下一次的调用就会返回下一个参数。参数 type 是一个已指定过的类型名(如 int , char ),这样一来,通过增加 * 到 type 中( 如 int *, char *)就可以简单地获得一个已指定类型对象的指针类型。
在调用 va_start() 后第一次使用 va_arg() 会返回 last 后的参数。连续调用则返回剩下的参数。
如果已没有下一个参数,或者 type 与下一个参数的实际类型并不兼容,则发生的错误不可预料。
如果把 ap 传递给一个调用 va_arg(ap, type) 的函数,那么当这个函数返回后,ap 的值为未定义。
va_end()
在同一个函数里,每一次调用 va_start() 都必须和 va_end() 相配对。在调用 va_end( ap ) 后,ap 变为未定义。对于多重遍历参数列表,则每次配对使用 va_start() 和 va_end() 是可以的。va_end() 可能是一个宏,或者是一个函数。
举例:#include <stdio.h>
#include <stdarg.h>
void foo(char *fmt, ...)
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
switch (*fmt++) {
case 's':
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd':
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c':
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
int main()
{
int val1 = 20;
char buf[20] = {"hello world"};
foo("kkkkkk%dxxxxxsxxxxx",val1,buf);
return 0;
}
运行及输出:beyes@linux-beyes:~/C> ./varg.exe
int 20
string hello world 说明:
在 foo() 函数中,使用一个 while 循环遍历 fmt 所指向字符串中的每一个字符,这是为了要找出与 switch 所定制相匹配的字符。假如找到匹配项,那么就调用 va_arg()。在 va_arg() 中,ap 表示了 fmt 指向字符串后面的参数列表。每一次调用 va_arg() 就会读出参数列表中的一个项,假如得到的这个参数列表中的项的实际类型和 va_arg() 中的第二个参数指明的类型一样,那么这个宏调用成功。如果是乱指定一个未知的类型,比如 kk ,那么这在编译时也会给出错误的提示:varg.c: In function ‘foo’:
varg.c:14: error: expected specifier-qualifier-list before ‘kk’ 如果指定一个不兼容的类型,比如把上面的 char * 改写成 int,编译时给出提示:varg.c: In function ‘foo’:
varg.c:14: warning: assignment makes pointer from integer without a cast 这是个警告信息,意思是没有通过类型强制转换就把一个整数类型变成指针类型。但程序运行后仍然打印出想要的信息。 |
|