C语言中有些函数的参数是可变的,可以接受不固定的任意参数,如stdio.h里的 printf函数。
例如:
printf(“ab”,”c”,ret,”%d “);
例:实现一个函数可以求任意个参数的平均值。
#include< stdio.h >
#include< strarg.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); //sum+=((int)((arg+=4)-4)
}
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(“avg1 = %d\n”, avg1);
printf(“avg2 = %d\n”, avg2);
return 0;
}
其中va_list 转到定义是 typedef char* 是char*的重命名,va_start转到定义处为 宏#define _crt_va_start(ap,v) \
( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
创建变量arg ,这个变量是调用 va_start 来初始化的。它的第一个参数是 va_list 的变量
名,第2个参数是省略号前最后一个有名字的参数。初始化过程把 arg 变量
设置为指向可变参数部分的第一个参数。
va_arg定义为#define _crt_va_arg(ap,t) \
( (t )((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
这个宏接受两个参数: va_list 变量和
参数列表中下一个参数的类型。在这个例子中所有的可变参数都是整型。
va_end定义为#define _crt_va_end(ap) \
( ap = (va_list)0 )
代码中sum += va_arg(arg, int)
宏替换后变为 sum+=((int)((arg+=4)-4)
arg指针指向函数栈帧上第一条 也就是 第一个变量a的地址
让它先加上一个整型的大小 指向第二个变量b的地址,再减去一个整形大小就指向第一个变量啊的地址 同时arg+=后直接变成第二个变量b的地址为下一次调用做准备。(int )强制类型转换后 再用“”简引用找出值相加。 最后调用 va_end使得变量arg变为空指针。
可变参数的限制
注意:
1.可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途终止,这是可以的,但是,如果你想一开始就访问参数列表中间的参数,那是不行的。
2.参数列表中至少有一个命名参数。如果连一个命名参数都没有,就无法使用va_start。
3.这些宏是无法直接判断实际存在参数的数量。
4.这些宏无法判断每个参数的是类型。
5.如果在va_arg中指定了错误的类型,那么其后果是不可预测的。
va_list
va_start
va_arg
三个宏配合使用可实现可变参数列表。