写在开头:
当我们想实现的函数功能的参数不确定时,我们就不能用同一个函数去实现不同参数的传递,此时,我们可以将函数实现成为可变参数的形式,可以使函数接受一个以上任意参数的传递。
举个栗子:
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。