va_list 是C语言中解决变参问题的一组宏(va,variable argument,可变参数),这些宏定义在 stdarg.h 文件中。主要包括:
void va_start( va_listarg_ptr, prev_param );
type va_arg( va_listarg_ptr, type );
void va_end( va_listarg_ptr );
(一) 示例
下面我们写一个简单的可变参数的函数,此函数至少有一个整数 参数,其他参数也是整型的,不过是可选的。void va_func(int i, ...)
{
va_list vl;
int j = 0;
va_start(vl, i);
j = va_arg(vl, int);
va_end(vl);
printf("%d %f\n", i, j);
return;
}
从这个函数的实现可以看到,使用可变参数应该有以下步骤:
1)在函数里定义一个va_list型的变量,这里是vl,这个变量是指向参数的指针.2)用va_start宏初始化变量vl.这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数.
3)用va_arg返回可变的参数. va_arg的第二个参数是你要返回的参数的类型,这里是float型.
4)如果函数有多个可变参数的,依次调用va_arg获取各个参数.
5)最后用va_end宏结束可变参数的获取.此时,你就可以在函数里使用第N个参数了.
1)va_func(100);
结果是:100 -123456789(会变的值)
2)va_func(100,200);
结果是:100 200
3)va_func(100,200,300);
结果是:100 200
我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果 正确,但和我们函数最初的设计有冲突.
(二)可变参数在编译器中的处理
va_list、va_start、va_arg、va_end是在stdarg.h中被定义成宏的,由于硬件平台及编译器的不同,所以宏的定义也有所不同。
示例:
typedef char * va_list;#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
示例解释:
_INTSIZEOF:主要用于某些需要内存对齐的系统。
va_list:被定义成char *,有一些平台或操作系统定义为void *.
va_start:&v是固定参数在堆栈的地址,_INTSIZEOF(v)是固定参数的内存大小,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆栈的中地址。
va_arg:(ap += _INTSIZEOF(t))将ap指向下一个参数;((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))获取本参数的地址;(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))将本参数的地址转换为t类型;( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )获取本参数的值。
(三)可变参数在编程中要注意的问题
va_list并不能智能地识别不同参数的个数和类型.
有人会问:printf中不是实现了智能识别参数吗?那是因为函数printf是从固定参数format字符串来分析出参数的类型,再调用va_arg的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.
(四) va系列宏的兼容性.
System V Unix 把 va_start 定义为只有一个参数的宏:va_start(va_list arg_ptr);
而 ANSI C 则定义为:
va_start(va_list arg_ptr, prev_param);
如果我们要用 System V 的定义,应该用 vararg.h 头文件中所定义的宏。ANSI C 的宏跟 System V 的宏是不兼容的。
参考文章:
http://jazka.blog.51cto.com/809003/232331/
http://blog.csdn.net/aihao1984/article/details/5953668