什么是可变参数?
就是通过将函数实现为可变参数的形式,可以使得函数可以接受1个以上的任意多个参数(不固定)。
- 比如下段代码:
int ret = average(3, 1, 2, 3);
ret = average(4, 2, 3, 6, 9);
- 这里想分别求两组数字个数不一样的平均值,那么这个函数的形参就需要用可变参数来实现;下面代码里面这个函数就可以实现这个问题
#include <stdarg.h>
int average(int n, ...)// ...表示未知参数
{
va_list arg;
//char * arg;
int i = 0;
int sum = 0;
va_start(arg, n);
//(arg=(char*)&n+4);
for(i=0; i<n; i++)
{
sum += va_arg(arg, int);
//sum += (*(int *)((arg +=4)-4))
}
va_end(arg);
//arg=(char *)0;
return sum/n;
}
int main()
{
int ret = average(3, 1, 2, 3);
printf("ret = %d\n", ret);//2
ret = average(4, 2, 3, 6, 9);
printf("ret = %d\n", ret);//5
return 0;
}
- 上面的va_list ,va_start ,va_arg ,va_end ; 这些我们平时不太见到的即为可变参数的语法。
- 可变参数列表是通过宏来实现的,这些宏定义于 stdarg.h 头文件,它是标准库的一部分。这个头文件声明了一个类型 va_list 和三个宏——va_start ,va_arg va_end 。
- 注意参数列表中的省略号:它提示此处可能传递数量和类型未确定的参数。
- 点击几个语法转到函数定义,我们则可以看到它最直白的意思;上面代码块的注释即为它转化之后的代码;
- VS源代码
可变参数的实现过程是使用宏的封装。只要完成替换,我们就可以自行分析了。 - 声明一个 va_list 类型的变量 arg ,它用于访问参数列表的未确定部分。
- 这个变量是调用 va_start 来初始化的。它的第一个参数是 va_list 的变量名,第2个参数是省略号前最后一个有名字的参数。初始化过程把 arg 变量设置为指向可变参数部分的第一个参数。
- 为了访问参数,需要使用 va_arg ,这个宏接受两个参数: va_list 变量和参数列表中下一个参数的类型。在这个例子中所有的可变参数都是整型。 va_arg 返回这个参数的值,并使用 va_arg 指向下一个可变参数
- 最后,当访问完毕最后一个可变参数之后,我们需要调用va_end。
- 可变参数的限制
- 注意:
- 可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途终止, 这是可以的,但是,如果你想一开始就访问参数列表中间的参数,那是不行的。
- 参数列表中少至有一个命名参数。如果连一个命名参数都没有,就无法va_start。
- 这些宏是无法直接判断实际存在参数的数量。
- 这些宏无法判断每个参数的是类型。
- 如果在va_arg中指定了错误的类型,那么其后果是不可预测的。