对C语言中va_list,va_start,va_arg和va_end的一点理解
这几个函数和变量是针对可变参数函数的,什么是可变参数函数呢,最经典的莫过于printf和scanf,这两个函数的声明如下:
1 int printf(const char *format, ...); 2 3 int scanf(const char *format, ...);
这两个函数声明中省略号(...)表示的就是任意个数的参数,可变参数函数就是输入的参数的个数是可变的,那么这个具体是怎么实现的呢?
要了解这个是怎么实现,首先我们就要先理解一点,参数是如何传递给函数的。众所周知,函数的数据是存放于栈中的,那么给一个函数传递传递参数的过程就是将函数的参数从右向左逐次压栈,例如:
func(int i, char c, doube d)
这个函数传递参数的过程就是将d,c,i逐次压到函数的栈中,由于栈是从高地址向低地址扩展的,所以d的地址最高,i的地址最低。
理解了函数传递参数的过程,再来说一下va_list的原理,通常,可变参数的代码是这么写的:
1 void func(char *fmt, ...) 2 { 3 va_list ap; 4 5 va_start(ap, fmt); 6 va_arg(ap, int); 7 va_end(va); 8 }
这里ap其实就是一个指针,指向了参数的地址。
va_start()所做的就是让ap指向函数的最后一个确定的参数(声明程序中是fmt)的下一个参数的地址。
va_arg()所做的就是根据ap指向的地址,和第二个参数所确定的类型,将这个参数的中的数据提取出来,作为返回值,同时让ap指向下一个参数。
va_end()所做的就是让ap这个指针指向0。
关于这三个参数实现的宏可以参看下面的实现:
1 // 使ap指向第一个可变参数的地址 2 #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) 3 4 // 使ap指向下一个可变参数,同时将目前ap所指向的参数提取出来并返回 5 #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) 6 7 // 销毁ap 8 #define va_end(ap) ( ap = (va_list)0 )
参考文章: