可变参数列表:
在我们一般经常使用的函数中,函数列出了期望接受的参数,但函数原型只能显示固定的参数,那么,如何让一个函数在不同的时候接受不同数目的参数呢!使用可变参数列表就可实现,当一个函数事先不确定有多少个参数但是可以接受一个或多个参数,可以使得函数可以接受1个以上的任意多个参数。
可变参数列表是通过宏来实现的,这些宏定义在stdarg.h头文件中,在这个头文件中声明了 一个类型va_list和三个宏va_start、va_arg、va_end配合使用,访问参数的值。
可变参数函数的原型声明格式为:type VAFunction(type arg1, type arg2, … ); 参数可以分为两部分:个数确定的固定参数和个数可变的可选参数。函数至少需要一个固定参数,固定参数的声明和普通函数一样;可选参数由于个数不确定,声明时用"…"表示。固定参数和可选参数共同构成一个函数的参数列表。
举个例子理解:比方求指定个数的平均值
#include <stdio.h>
#include <stdarg.h>
int average(int n,...) //定义一个函数,实现可变参数
{
va_list arg; //定义一个变量arg为va_list类型
int i = 0;
int sum = 0;
va_start(arg, n);
for(i=0; i<n; i++) //循环获取参数
{
sum += va_arg(arg, int);
}
return sum/n;
va_end(arg);
}
int main()
{
int ret1 = 0;
int ret2 = 0;
ret1 = average(3, 1,2,3);
ret2 = average(4, 2,3,3,5);
printf("%d\n",ret1);
printf("%d\n",ret2);
return 0;
}
程序结果
在vs中我们可以转到定义处查看各个类型和宏具体是怎样实现的
1、首先va_list arg;
#elif defined(_M_IX86)
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
#elif defined(_M_IA64)
很明显va_list就是一个类型重命名;va_list实际上就是char*型,简言之va_list arg就是声明了一个字符型指针arg。
2、va_start(arg, n);
#elif defined(_M_IX86)
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
#elif defined(_M_IA64)
_INTSIZEOF(n)整个做的事情就是将n的长度化为int长度的整数倍
va_srart(ap,v)就是把上面的字符指针向后移动,跳过第一个参数n的地址。
3、va_arg(args, int)
#define _crt_va_end(ap) ( ap = (va_list)0 )
va_arg(args, int) 就是循环获取到可变参数列表中的参数,args指向下一个参数地址,返回的则是当前参数地址。
4、va_end(arg);
#define _crt_va_end(ap) ( ap = (va_list)0 )
当访问完毕最后一个参数时,用VA_END宏结束可变参数的获取。
可变参数列表的缺陷:
1、可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途
终止,这是可以的,但是,如果你想一开始就访问参数列表中间的参数,那
是不行的
2、参数列表中至少有一个命名参数。如果连一个命名参数都没有,就无法使用
va_start。
3、如果在va_arg中指定了错误的类型,结果无法预测,因为在使用时,char、short、float类型的值实际上都作为int或double类型的值传递给函数。