可变函数参数复习
首先,来看一个可变参数函数:
#include <stdio.h>
#include <stdarg.h>
int Max(int n,...);//可变参数函数,求最大值
int main(int argc,char *argv[])
{
int max=Max(5,2,1,6,3,5) ;
printf("%d\n",max);
return 0;
}
int Max(int n,...)
{
int i=0,max=0,num=0;
va_list arg; //与 cahr *arg;等价
va_start(arg,n); //arg指向第一个可变参数 2
max=va_arg(arg,int);
for(i=0;i<n;i++)
{
num=va_arg(arg,int);
if(max<num)
{
max=num;
}
}
va_end(arg); //arg=NULL;
return max;
}
这个函数很简单,就是求最大值。特点就是可以输入任意个参数,而没有像以往的函数一样固定参数个数。
查看代码,我们发现几个没见过的东东:
va_list
va_start
va_arg
va_end
"…"符号
声明一下,这些都不是函数,而是宏定义
va–variable argument(可变的参数)
(1)va_list
查看stdarg.h文件
typedf char* va_list;
所以,va_list其实是一个指向char类型的指针。
(2)va_start()
查看stdarg.h文件
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
再次强调,这是宏,宏,宏!不是函数!
看到有点头大吧!来,分析分析:
首先:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
[推荐答案]
_INTSIZEOF(n)整个做的事情就是将n的长度化为int长度的整数倍。
比如n为5,二进制就是101b,int长度为4,二进制为100b,那么n化为int长度的整数倍就应该为8。
~(sizeof(int) - 1) )就应该为(4-1)=(00000011b)=11111100b,这样任何数& ~(sizeof(int) - 1) )后最后两位肯定为0,就肯定是4的整数倍了。
(sizeof(n) + sizeof(int) - 1)就是将大于4m但小于等于4(m+1)的数提高到大于等于4(m+1)但小于4(m+2),这样再& ~(sizeof(int) - 1) )后就正好将原长度补齐到4的倍数了。
原文地址:
我们知道对于IX86,sizeof(int)一定是4的整数倍,所以~(sizeof(int) - 1) )的值一定是 右面[sizeof(n)-1]/2位为0,整个这个宏也就是保证了右面[sizeof(n)-1]/2位为0,其余位置
为1,所以_INTSIZEOF(n)的值只有可能是4,8,16,…等等,实际上是实现了字节对齐。
_INTSIZEOF(n) 的目的在于把sizeof(n)的结果变成至少是sizeof(int)的整倍数,这个一般用来在结构中实现按int的倍数对齐
如果sizeof(int)是4,那么,当sizeof(n)的结果在14之间是,_INTSIZEOF(n)的结果会是4;当sizeof(n)的结果在58时,
_INTSIZEOF(n)的结果会是8;当sizeof(n)的结果在9~12时,_INTSIZEOF(n)的结果会是12;……总之,会是sizeof(int)的倍数。
走远了,再回来讲va_start():
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
ap是类型为va_list的指针,v是可变参数最左边的参数,亦即最后一个固定参数。
运行完这句代码后,ap指向第一个可变参数在堆栈中的地址。
|------------------------------------------------|高地址
|-------------函数返回地址------------------|
|------------…-----------------|
|------------------------------------------------|<–va_arg后ap指向下一个参数
|----- 第n个参数(第一个可变参数)-----|
|------------------------------------------------|<–va_start后ap指向第一个可变参数
|— 第n-1个参数(最后一个固定参数)----|
|------------------------------------------------|<–&v 低地址
所以,va_start(arg,n);运行后,arg指向了第一个可变参数“2”。
(3)va_arg(arg,int)
#define va_arg(ap,t) (*(t*)((ap+=_INTSIZEOF(t))- _INTSIZEOF(t)))
va_arg()取可变参数的值。
代码精髓:
*( t )((ap+=_INTSIZEOF( t ) ) - _INTSIZEOF(t))
首先,ap += sizeof( t ),ap已经指向下一个参数的地址了(ap的值改变);
然后,ap-sizeof( t ),又返回前一个参数了(ap的值没有改变),用取出参数的值。
一句代码,其实执行了两步,妙哉妙哉!
(4)va_end(arg)
#define va_end(ap) ( ap = (va_list)0 )
使ap不再指向堆栈,而是跟NULL一样。
OK,关于可变函数参数就到这了!
思考:printf()怎么实现的呢!写出自己的myprintf()函数吧!