文章标题

来源:互联网 发布:单片机开发板哪个好 编辑:程序博客网 时间:2024/05/29 03:32

可变函数参数复习

首先,来看一个可变参数函数:

#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的倍数了。

原文地址:

http://blog.csdn.net/sdcxyz/article/details/7067699

我们知道对于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)的结果在1~4之间是,_INTSIZEOF(n)的结果会是4;当sizeof(n)的结果在5~8时,
_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()函数吧!

0 0
原创粉丝点击