关于INTSIZEOF宏是怎么来的已经解释过了, 这里简要概述一下,宏定义如下:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
当sizeof(n)<4时,宏的值就是4,当sizeof(n)>4时,宏的值就是4的倍数。
这里主要介绍两个宏va_start和va_arg两个宏,先说这个va_start。
宏定义如下:
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ),这里的va_list的类型是
char *,v是输入的第一个参数。首先在理解这个宏的前提要理解函数的形参是如何存储,这里我们做个试验如下图所示
形参是存储在栈里面的,栈的高地址在上,低地址在下,那么由上图可以得到的是,x,y,z在栈中存储的结构从上到下依次为z,y,x。那么(va_list)&v的意思就是取第一个参数的地址并强制转化为char*类型。比如这里的x的地址为1638116(十进制表示的)被强制转化为char*,对于_INTSIZEOF(v),若是这里的v也为x的话,那么这个宏的值为4。进而两者相加的值就是1638120。这里要说明的是地址的类型为char*的话,地址加1就相当于只是加1,比如这里的1638116 + 4 = 1638120;如果地址的类型是int *那么加1的意思就是地址+1*4。为什么???是这样的:比如一个指针指向一个int的输入比如int a[10],那么每一个元素就占4个字节所以地址+1中的1表示1*4,所谓的指针加几就是为了向前或者向后移动,那么移动多少字节就要取决于指针指向的变量的类型了,这样解释明白了吧!!!很基础是吧,比如数组是char型的,那么加1就是地址纯粹的加1了,因为sizeof(char) = 1的嘛。
好了,继续上面说的得到了地址1638116 + 4 = 1638120,那么这个1638120不正好就是y的地址值吗。所以这个宏的功能就是:输入第n个参数,输出的就是第n + 1个参数的地址值。再来介绍va_arg宏,它的定义如下:
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ),这里的t输入的是变量的类型,比如int,char等等之类的。ap += _INTSIZEOF(t)的意思让指针ap指向下一个参数的地址,在强调一遍高地址存的是函数越右边的形参值(因为形参是存在栈中的),比如假设ap指向的是x的地址,要使得ap指向y,那么由于x是在低地址那么就要加上x的类型,比如这里的x类型为int,所以这个时候ap是指向y的了。再减去_INTSIZEOF(t)(注意此时ap还是指向的y)那么((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))整个地址值就还原到原来的地方,也就是再次指向了x,为什么又要回来,其实就是为了使得ap指向下一个地址,然后再次回到原来的地址,在经过强制转化(t *),然后再取值*(t *)的符号“*”。
我们来捋一捋这个过程,假设函数只有x,y,z三个形参,一般先调用va_start使得ap指向了y,然后再调用va_arg,使得ap指向z,由于y的值没取出来进行计算,那么,就要把地址还原也就是上面再减去_INTSIZEOF(t),在取出y的值。那么这里就有一个问题是x的值并没用到,别急看完下面的例子就明白怎么用x的值了。
我们来举一个例子来应用一下,我们要实现,不管输入多少个参数,使得函数形参从第二个参数开始起进行累加,如下图所示:
function函数形参x表示的是有多少个参数,因为在执行va_start的时候就直接使得ap的指针指向了下一个形参的位置,所以可以利用x的值来表示有多少个参数,比如这里有4个参数,分别为0,1,2,3,4,那么function的功能就是把这几个值相加也就是6了。
举报/反馈