有时,我们需要实现一个函数并希望这个函数可以传递可变数量的参数,就需要用到可变参数。它允许定义一个函数,能根据需求具体接受可变数量的参数。如下:
int fun(int n, ...)
{
//...
}
int main()
{
fun(2,3,4);
fun(4,1,2,5,6);
return 0;
}
fun()
函数中的...
代表可变参数,省略号前的int n
代表要传递的可变参数的个数。
C语言中可以将函数实现为可变参数的形式,从而达到接受1个以上的任意多个参数的目的。
先来看一个例子:
实现一个函数可以求任意个参数的平均值。
#include <stdio.h>
#include <stdarg.h>
int average(int n, ...)
{
va_list arg;
int sum = 0;
int i = 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 = 0;
ret = average(5,1,2,3,4,5);
printf("%d\n",ret);
return 0;
}
首先我们来明确一下上面出现的va_list
、 va_start
、va_arg
、 va_end
都是起什么作用的:
va_list
的汇编代码中就是char *
typedef char * va_list;
简单来说就是声明一个va_list
类型的变量arg
,它用于访问参数列表的未确定部分。
va_start
的汇编代码中如下:
#define va_start _crt_va_start
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
宏定义中_ADDRESSOF(v)
就是&v
,_INTSIZEOF(v)
在本例中是4,va_list
是char *
;所以va_start
的作用是初始化arg
为指向未知参数列表第一个参数的地址。它的第一个参数是va_list
的变量名,第二个参数是省略号前最后一个有名字的参数。
va_arg
为了访问参数,需要使用va_arg
,它的汇编代码为:
#define va_arg _crt_va_arg
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
宏定义中的( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
在本例中可等价为
( *(int *)((arg += 4) - 4) )
,这个宏接受两个参数:va_list
变量和参数列表中下一个参数的类型,本例中所有的可变参数都是整型。
va_arg
返回这个参数的值,并使va_arg
指向下一个可变参数。
va_end
当访问完毕最后一个可变参数之后,需要调用va_end
,它的汇编代码如下:
#define va_end _crt_va_end
#define _crt_va_end(ap) ( ap = (va_list)0 )
va_end
就是把arg
置为空指针。
注意:
可变参数必须从头到尾逐个访问。(如果你访问了几个可变参数后不想访问了,这样是可以的;但要是从一想开始就访问列表中间的参数不不可以的)
参数列表中至少有一个命名参数。(如果连一个命名参数都没有,就无法使用
va_start
)这些宏是无法直接判断实际存在参数的数量。
这些宏无法判断每个参数的类型。
不要在
va_arg
中指定了错误的类型,否则结果不可预测。使用可变参数需要引用头文件
#include <stdarg.h>
。
最后来一道题再练习一下可变参数:
使用可变参数,实现函数,求函数参数的最大值。
#include <stdio.h>
#include <stdarg.h>
int max(int n, ...)
{
va_list arg;
int max = 0;
int i = 0;
va_start(arg, n);
for(i=1; i<n; i++)
{
int tmp = 0;
if((tmp = va_arg(arg, int))>max)
max = tmp;
}
return max;
}
int main()
{
int ret = 0;
ret = max(5, 1, 2, 6, 3, 4);
printf("%d\n",ret);
return 0;
}
END