可变参数列表通过宏来实现,这些宏定义在头文件#include< stdarg.h >中,包含一个类型va_list(vs中为char*),三个宏va_start,va_arg,va_end。声明一个va_list的变量和三个宏配合使用
首先我们看一个简单的用可变参数写的求平均值的函数:
#include<stdarg.h>
int average(int value,...)
{
va_list var_arg;//定义一个变量
int sum = 0;
va_start(var_arg,value);//初始化
for (int i = 0; i < value; ++i)
{
sum += va_arg(var_arg,int);//返回当前参数的值,并且让var_arg指向下一个可变参数
}
va_end(var_arg);//访问玩可变参数列表需要调用
return sum/value;
}
- va_list var_arg;//定义一个变量
va_list–>typedef char * va_list(vs2013) va_start(var_arg,value)
初始化,把变量var_arg设置为指向可变参数部分的第一个参数
第一个参数为va_list定义的变量名
第二个参数为省略号前最后一个有名字的参数sum += va_arg(var_arg,int)
返回当前参数的值,并且让var_arg指向下一个可变参数
第一个参数为va_list定义的变量名
第二个参数为参数列表中下一个参数的类型- va_end(var_arg)
访问完可变参数列表需要调用
可变参数在编译器内部的处理:
typedef char * va_list;
#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
//(va_list)_ADDRESSOF(v) ==> (va_list)&(v)然后再加_INTSIZEOF(v)ap指向第一个可变参数
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
//首先ap+=_INTSIZEOF(t),已经指向下一个参数的地址了.然后返回ap-_INTSIZEOF(t)的int*指针,然后用*取得这个地址的内容(参数值)
#define _crt_va_end(ap) ( ap = (va_list)0 )
注意点:
因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型.有人会问:那 么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的宏是不兼容的,我们一般都用ANSI C,所以用ANSI C的定义就够了,也便于程序的移植.
在C++里,利用C++的多态性来实现可变参数的功能