写在开头:
当我们想实现的函数功能的参数不确定时,我们就不能用同一个函数去实现不同参数的传递,此时,我们可以将函数实现成为可变参数的形式,可以使函数接受一个以上任意参数的传递。
举个栗子:
int main()
{
int ave = 0;
ave = average(2, 2,2);
printf("%d\n", ave);
ave = average(3, 2,2,2);
printf("%d\n", ave);
system("pause");
return 0;
}
当我们要求平均数的时,需要调用两次average函数,第一次有四个参数,第二次有三个参数,这时可以用可变参数的形式来进行函数实现。
可变参数的形式:
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);
}
return sum / n;
va_end(arg);
}
现在来解释一下这段代码的含义(在vc下查看比较直观),因为这个编译器比较久了,封装的比较简单,易于查看。我们先来了解几个在代码中出现的不熟悉的地方。
代码解释:
○选择va_list右击转到定义,可以看到 typedef char * va_list;类型重命名va_list给char*,则可以替换掉。
○选择va_start同样右击转到定义查看,是一个宏定义
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
我们可以替换掉( arg = (char *)&n + _INTSIZEOF(n) );其中_INTSIZEOF(n)这也是一条宏定义,其含义为取4的整数倍,比如当n为1,2,3,4,时,这个值取4;为5,6,7,8时就取8.整条语句可以替换为 arg=(char*)&n+4 arg 来到了未知参数部分的第一个参数,找到了起始位置。
○选中va_arg右击转到定义,也是一个宏定义
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )替换为 ( *(int *)((arg += 4) - 4 ),arg向后跳四个字节即int的字节数,指向第二个参数位置,然后-4还是第一个参数的值,但agr已经指向第二个位置了
○
选中va_end转到定义查看,#define va_end(ap) ( ap = (va_list)0 )替换为(arg=(va_list)0);
使用宏替换之后的代码如下图:
#include <stdarg.h>
#include <stdio.h>
int average(int n, ...)
{
//va_list arg;
char *arg;
int i=0;
int sum=0;
//va_start(arg, n);
//#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
//#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
( arg = (char *)&n +4 ); // _INTSIZEOF(n)=4,
for (i = 0; i < n; i++)
{
//sum = sum + va_arg(arg, int);
sum +=( *(int *)((arg += 4) - 4 ));//arg向后跳四个字节即int的字节数,指向第二个参数位置,然后-4还是第一个参数的值,但agr已经指向第二个位置了
}
//va_end(arg);
(arg=(char*)0);//赋成空指针
return sum / n;
}
int main()
{
int ave = 0;
ave = average(2, 2,2);
printf("%d\n", ave);
ave = average(3, 2,2,2);
printf("%d\n", ave);
return 0;
}
总结:
※ 声明一个va_list类型的变量arg,它用于访问访问参数列表的未知部分。
※ 调用va_start来对arg进行初始化,它的第一个参数是va_list的变量名arg,第二个参数是省略号前最后一个有名字的参数。初始化过程中arg变量设置为指向可变参数部分的第一个参数。
※ 访问参数需要使用va_arg,这个宏接受两个参数:va_list变量和参数列表中下一个参数类型。在这个例子中,所有可变参数都是整型。va_arg返回这个参数的值,并指向下一个可变参数。
※ 最后,访问完最后一个可变参数时,需要使用va_end。