VA_LIST 是在c语言中解决变参问题的一组宏,所在头文件:#include <stdarg.h>
变量定义:
#ifdef _M_ALPHA
typedef struct {
char *a0; /* pointer to first homed integer argument */
int offset; /* byte offset of next parameter */
} va_list;
#else
typedef char * va_list;
#endif
_M_ALPHA是指DEC ALPHA(Alpha AXP)架构。所以一般情况下va_list所定义变量为字符指针。
几个宏定义:
INTSIZEOF 宏
,获取类型占用的空间长度,最小占用长度为int的整数倍:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数):
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型):
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
VA_END宏,清空va_list可变参数列表:
#define va_end(ap) ( ap = (va_list)0 )
函数参数的传递原理:
函数参数是以数据结构:栈的形式存取,从右至左入栈;
举例说明:
先介绍一下可变参数表的调用形式以及原理:
首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
void func(int x, float y, char z);
那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
用法:
(1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
例如:va_list ap;
(2)然后用VA_START宏初始化刚定义的VA_LIST变量
;
然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个
参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
例如:
假设可变参数函数定义如下:int fun(int start,...)包括一个固定参数为int类型的start变量。
va_start( ap, start)
此时的ap执行start下一个可变参数的地址。
(3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用
VA_ARG获取各个参数);
然后是
获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且
把
ap 的位置指向变参表的下一个变量位置;
例如:
假设可变参数函数定义如下:int fun(int start,...)包括一个固定参数为int类型的start变量。
va_start( ap, start)
此时的ap为start下一个可变参数的地址。
va_arg( ap, int)
此时ap为start下两个可变参数的地址,每执行一次va_arg,ap地址指向当前可变参数的下一个参数地址,
同时返回当前地址的可变参数的值;
(4)最后用VA_END宏结束可变参数的获取。
例如: va_end(ap);
测试代码实例:test.c
#include<stdarg.h>
#include<stdio.h>
int sum(int,...);
int main()
{
printf("Sum of 25 and 32 = %d", sum(2, 25, 32));
return0;
}
int sum(int num_args,...)
{int val =0;
va_list ap;
int i;
va_start(ap, num_args);//ap指向第一个可变参数的地址
for(i =0; i < num_args; i++)
{
val += va_arg(ap,int);//返回当前ap地址的值,并且将ap指向下一个可变参数的地址
}
va_end(ap);//结束获得可变参数
return val;
}
让我们编译和运行上面的程序,这将产生以下结果:
gcc test.c -o test
./test
Sum of 25 and 32 = 57