定义:什么叫可变参数列表呢,顾名思义,可变参数,就是函数可以接受的参数是可变的,可以接受1个以上任意多个参数。(至少是一个)
接下来先看一个列子:
#include <stdio.h>
#include <stdarg.h>
int average(int n,...)
{
va_list arg;
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 a = 1 ;
int b = 2 ;
int c = 3 ;
int avg1 = average(2 , a, c);
int avg2 = average(3 , a, b, c);
printf ("%d \n" , avg1);
printf ("%d \n" , avg1);
return 0 ;
}
上面的函数实现的是求任意个整数的平均值,因为传的参数不是固定的(但至少要传一个,且知道类型,原因稍后解释)
要想深入了解可变参数,首先我们必须对函数的栈帧结构有一定的了解,我们来看下面这张图
这张图表示的是main函数调用myfun函数形成栈帧结构的过程,同理上面代码中的main调用average函数也会形成类似的栈帧结构,对函数栈帧有了解的都应该知道,函数参数实例化是从右到左的,(即如上图中a,b传过来a是后过来,即a在下面(低地址))而上面的可变参数,至少要传一个,这样我们就可以通过传过来n,来间接找到传过来的参数,不管传几个,我们都可以通过指向n的指针再加上对应的类型来找到相应的参数。例如:我们要通过a的地址找到b,就可以这样做,*p=&a,(p+1)(+1指的是加上指针所指类型的大小)再解引用就是b了。
接下来在解释上面代码中宏函数有什么作用:
va_list arg:这里arg的类型一定是 char * ,为什么是char * ,这很容易理解,因为char *指针加减1,就是增加或减少1,适用于找到各种类型的参数。
va_start(arg, n):这里先将n的地址赋给arg,再将arg加上n的大小,从而让指针指向不确定的部分。
va_arg(arg, int):这个宏函数的作用是通过类型提取指定参数,怎么做到的呢?上面我们已经将arg指向了不确定部分,即n的下一个参数(假设下面的参数分别是a, b, c….)a,要想提取a,我们可以先将指针指向的类型强转为a的类型,然后解引用就可就提取出a,arg再加上a的大小,然后再将指针指向的类型强转为b的类型,然后再解引用就可以提取出b,同理,可以提取出接下开的参数。
va_end(arg):作用就是将指针arg设置为NULL,这里不多做解释。
可变参数的限制
注意:
可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途终止,这是允许的,但是,如果你想一开始就访问参数列表中间的参数,那是不行的。
1.参数列表中至少有一个命名参数。如果连一个都没有,就无法使用va_start。
2.这些宏是无法直接判断实际存在参数的数量。
3.这些宏无法判断每个参数的类型。
1.如果va_arg中指定了错误的类型,那么其后果是不可预测的。