此乃讨论贴。首先介绍下我的理解。变参函数参数看起来是这样
<函数返回值类型> 函数名 (固定形参1,固定形参2,...)比如
声明
void multiply (int factor1, int factor2, ...)
函数体
void multiply (int factor1, int factor2, ...)
{
}
在调用他的函数里
multiply(1,2,3,4); /*多少都行,但必须至少是2个,因为声明里固定参数有2个*/
多出来的两个参数,需要用到一种特殊的数据类型来访问(也有其他方法)
void multiply (int factor1, int factor2, ...)
{
va_list ap; /* va_list 类型变量ap,其实木有啥,就是一正常的指针类型变量,赋值后应该是指向参数列表,以后个就用ap指针来访问变参了,跟其他指针 * 类型变量声明一样,他现在没有指向任何地方*/
va_start (ap, factor2); /* ap现在指向 factor2后面的一个参数了,va_start作用只是指针ap赋值这里小啰嗦一下,va_start赋值时,应当是指向最后一个固定参数(这里 * 是factor2,factor1是第一个固定参数)因为只有变参才需要指针访问他,固定参数直接用参数名就好了嘛*/
int factor3 = *((int *)ap);
factor3 = va_arg(ap, int); /* 这两种方法factor3值相同,不同的是va_arg返回的值是ap当前,并且将ap指向下一个参数,通过的方法是*ap++ */
...
va_end(ap) /* ap再也不用了*/
}
最近查阅了几个博文关于变参函数和va_list 的使用,发现有一些矛盾之处。
首先是堆栈中参数存储顺序。根据
http://blog.csdn.net/zckloveczy/article/details/4260698
高地址 最后一个参数
倒数第二个参数
...
第一个参数
函数返回地址
低地址 函数体
然后根据
http://wenku.baidu.com/view/e79b720f52ea551810a68783.html
高地址 函数返回地址
最后一个参数
倒数第二个参数
...
低地址 第一个参数
这两处矛盾在于函数返回地址和参数列表比,谁的地址更高?
第二个有趣的现象是,va_end 这个宏,我觉得他应该是像delete一样,先把指针删了,再指向null,可代码中,他什么都没有做。并且当我用完va_end(ap)后,用*((int *)ap)居然能看到他的值
在VC++6.0的include有一个stdarg.h头文件,有如下四个宏定义:
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
/* int型占的字节数,不大于4的是4,大于4小于等于8的是8 */
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
/* v是最后一个固定参数,加上他按int字节数对齐后,就是第一个可选参数地址 */
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
/* 第一步ap += _INTSIZEOF(t),ap指向下一个形参,命名为n,第二步,将当前参数的值返回 */
#define va_end(ap) ( ap = (va_list)0 ) // 将指针置为无效
在内核的文件里
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap) (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))