可变参数列表剖析
在函数的原型中,列出了函数期望接受的参数,单元形只能显示固定数目的参数。让一个函数在不同的时候接受不同数目的参数是不是可以呢?答案是肯定的,但存在一些限制。
举个例子:
float_average(int n,int v1,int v2,int v3,int v4,int v5)
{
float sum = v1;
if(n>2)
sum += v2;
if(n>3)
sum += v3;
if(n>4)
sum += v4;
if(n>5)
sum += v5;
return sum/n;
}
这个函数存在很多缺陷,首先,它不对参数数量进行测试,无法检测到参数过多的这种情况。其次,它无法处理超过五个的值。
当你试图用下面这种形式调用这个函数时,还会存在更严重的一个问题:
int avg = average(3,1,2,3);
这里只存在四个参数,但函数具有6个形参。
为了解决这个问题,我们需要一种机制,他能够以一种良好的定义方式访问参数未定的参数列表。
stdarg 宏
可变参数列表是通过宏实现的,这些宏定义于 stdarg.h 头文件。这个头文件声明了一个类型 va_list 和三个宏va_start,va_arg,va_end。
同样是求平均数,让我们看一段代码
#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);
va_end(arg);
return sum/n;
}
int main()
{
int ret = average(5,1,2,3,4,5);
return 0;
}
va_list定义了一个arg变量,它用于访问参数列表未确定的部分。va_start对其进行初始化,其第一个参数是va_list变量的名字,第二个参数是省略号前第一个有名字的参数。在初始化的过程,arg被设置为指向可变参数部分第一个参数。为了访问访问参数,我们需要使用va_arg。这个宏接受两个参数,va_list定义的参数和列表中下一个参数的类型。va_arg返回这个参数的值,并且指向下一个可变参数。最后,访问完最后一个参数时,需要调用va_end将指针置为无效。
va_start宏的定义如下
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
_INTSIZEOF(n)作用:将n的长度化为int长度的整数倍。
va_arg宏定义如下
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
va_arg作用:返回参数的值,并使arg指向下一个参数。
va_end定义如下
#define va_end(ap) ( ap = (va_list)0 )