尝试使用C语言可变参数列表
对于一些函数有的时候,我们不希望它只能传递固定参数,例如如果我们想计算2个整数数之和写一个int sum(int ,int );的函数,但是如果想计算3个数之和呢?n个数之和呢?不可能去定义无限多个函数,也不能让函数的参数太多,形如int sum(int ,int ,int ,int ,int ,int ,int ,int ,int ,int );10个数求和,这样的编码既不方便也不优雅,我们希望定义一个函数,实现参数个数可以任意变化而不影响程序功能的函数。这就需要C语言定义的可变参数列表来实现了。
可变参数列表的使用必须使用头文件 stdarg.h 该头文件包含有可变参数列表的宏定义和变量定义,包括va_list可变参数列表类型 va_arg() va_start() va_end() 3个宏定义
下面展示求和函数源代码:
#include<stdio.h>
#include<stdarg.h> //包含有可变参数列表的宏定义
sum(int length,...)
{
int sum=0; //求和的临时变量
va_list va; //可变参数列表变量
//指向values的下一个元素,在入栈规则下,这个元素为values右边第一个参数
va_start(va,length);
while((length--)>0) //values代表总共有多少个参数参与求和,不包括它本身
{
sum+=va_arg(va,int); //迭代求和
}
va_end(va); //使指针指向空防止未定义指针错误
return sum;
}
int main()
{
printf("sum=%d",sum(10,2,3,4,5,2,3,4,5,64));
return 0;
}
原理
可变参数列表实际上是利用了C语言函数参数传递时的入栈顺序实现的,函数运行时,分配好的栈区的栈底在高地址,入栈向低地址扩展,地址减小(--),出栈则地址增加(++),调用函数时函数参数讲从右至左依次入栈,在本例中length就是最后一个入栈的地址最低,所以,只需要对length的地址进行自增运算,在保证所有参数类型一致的情况下即可把所有参数都访问到。
如本例:
va_list 定义一个可变参数列表的结构梯变量va_arg。
,然后使用va_start()这个带参宏来使va_arg结构体中的地址指向length之后的第一个参数的地址,在本例中就是参数2的地址。
然后我们就可以根据循环来迭代计算(栈空间是连续的)每次使用va_arg()这个带参宏来访问该元素,参数int是为了确定每次指针指向跳过的字节数。
最后,我们使用va_end()这个带参宏来使va结构体中的地址为NULL,防止为定义的行为发生。
所以,可变参数列表的原理就是简单的函数调用时参数在内存中存放规则(入栈规则)。
至于更复杂的printf(const char * format,...)可以接受不同类型的参数,是可变参数列表的经典实例。
未完待续!