首先通过一个例子来了解可变列表参数
一个函数可以求出任意个参数的平均值:
#include<stdio.h>
#include<stdlib.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 = sum + va_arg(arg, int);
}
va_end(arg);
return sum/n;
}
int main()
{
int x = average(4, 1, 2, 3, 10);
printf("%d", x);
system("pause");
return 0;
}
首先我们来分析一下这个函数,给函数传参时,你会发现,只有实参4被传给了n,而4之后的实参传过去都被...代替,这是什么意思呢?,其实第一个实参也就是4,代表的是在它之后实参的个数。而...代表的就是4之后的实参传过去的形参,这也是它为什么可以传任意个参数。
然后进入函数内部,这里可能有许多你不认识的代码,但是没关系,我们一句一句来分析。
第一个是 va_list arg; 我们看一下它的定义
这里我们可以看出来va_list其实就是char*,这句代码的意思是声明一个char*类型的变量arg。
接下来的两句就是定义并初始化两个整形变量i和sum,
再下来的一句 va_start(arg, n); 可能又看不懂,但是没关系,我们转到定义
我们看到的是这个定义,但是_crt_va_start我们仍然不认识,当我们选中它再次转到定义是就是这样
这里我们可以看出va_start的作用,其中_ADDRESSOF()转到定义是取地址的意思,_INSIZEOF()的意思是当你要占用的最大的字节数,这里是整形所以_INSIZEOF()等于4,这句话替换之后就位 arg = (char*)&n+4,这句话什么意思呢,只有你学过栈帧(如下图所示),你就明白它代表的是第二个形参。其实va_start()的作用是将arg初始化。让arg指向你第一个要计算的形参。
接下来是一个for循环,va_arg的用法从上面的两张图可以看出,它可以替换为 *(int *)((arg = arg + 4)-4),其中(arg = arg + 4)的意思是指向下一个要计算的形参,但是为什么要-4呢,因为初始化时你已经指向了第二个形参,它是要进行计算的如果不-4回到原来的位置的话,就会漏掉第二个形参,使你的计算结果错误。最后将它强制类型转化为int型并解引用。
当它计算完之后 回来到va_end();他的意思从转到定义的图中可以看出,它的作用是将arg指针指向空。
可变参数的限制
1.可变参数必须从头到尾逐个访问,如果你想访问几个参数后版图停止也可以,但是如果你想一开始就访问中间的参数是不可以的.
2.参数列表中至少有一个命名参数,如果连一个命名参数都没有的话,就无法使用va_start,
3.这些宏是没有办法判断实际存在的参数的个数和类型。
4.一定要在va_arg中的括号里写入正确的类型,不然后果无法想象。