资源描述:
《从printf谈可变参数的实现.pdf》由会员上传分享,免费在线阅读,更多相关内容在行业资料-天天文库。
1、从printf谈可变参数函数的实现作者:戎亚新摘要:一直以来都觉得printf似乎是c语言库中功能最强大的函数之一,不仅因为它能格式化输出,更在于它的参数个数没有限制,要几个就给几个,来者不拒。printf这种对参数个数和参数类型的强大适应性,让人产生了对它进行探索的浓厚兴趣。关键字:printf,可变参数1.使用情形inta=10;doubleb=20.0;char*str="Helloworld";printf("beginprint");printf("a=%d,b=%.3f,str=%s",a,b,str);...从printf的使用情况来看,我们不难发现一个规律,就
2、是无论其可变的参数有多少个,printf的第一个参数总是一个字符串。而正是这第一个参数,使得它可以确认后面还有有多少个参数尾随。而尾随的每个参数占用的栈空间大小又是通过第一个格式字符串确定的。然而printf到底是怎样取第一个参数后面的参数值的呢,请看如下代码2.printf函数的实现//acenv.htypedefchar*va_list;#define_AUPBND(sizeof(acpi_native_int)-1)#define_ADNBND(sizeof(acpi_native_int)-1)#define_bnd(X,bnd)(((sizeof(X))+(bnd))&(~
3、(bnd)))#defineva_arg(ap,T)(*(T*)(((ap)+=(_bnd(T,_AUPBND)))-(_bnd(T,_ADNBND))))#defineva_end(ap)(void)0#defineva_start(ap,A)(void)((ap)=(((char*)&(A))+(_bnd(A,_AUPBND))))//start.cstaticcharsprint_buf[1024];intprintf(char*fmt,...){va_listargs;intn;va_start(args,fmt);n=vsprintf(sprint_buf,fmt,args
4、);va_end(args);write(stdout,sprint_buf,n);returnn;}//unistd.hstaticinlinelongwrite(intfd,constchar*buf,off_tcount){returnsys_write(fd,buf,count);}3.分析从上面的代码来看,printf似乎并不复杂,它通过一个宏va_start把所有的可变参数放到了由args指向的一块内存中,然后再调用vsprintf.真正的参数个数以及格式的确定是在vsprintf搞定的了。由于vsprintf的代码比较复杂,也不是我们这里要讨论的重点,所以下面就不再列出
5、了。我们这里要讨论的重点是va_start(ap,A)宏的实现,它对定位从参数A后面的参数有重大的制导意义。现在把#defineva_start(ap,A)(void)((ap)=(((char*)&(A))+(_bnd(A,_AUPBND))))的含义解释一下如下:va_start(ap,A){char*ap=((char*)(&A))+sizeof(A)并int类型大小地址对齐}在printf的va_start(args,fmt)中,fmt的类型为char*,因此对于一个32为系统sizeof(char*)=4,如果int大小也是32,则va_start(args,fmt);相当
6、于char*args=(char*)(&fmt)+4;此时args的值正好为fmt后第一个参数的地址。对于如下的可变参数函数voidfun(doubled,...){va_listargs;intn;va_start(args,d);}则va_start(args,d);相当于char*args=(char*)&d+sizeof(double);此时args正好指向d后面的第一个参数。可变参数函数的实现与函数调用的栈结构有关,正常情况下c/c++的函数参数入栈规则为__stdcall,它是从右到左的,即函数中的最右边的参数最先入栈。对于函数voidfun(inta,intb,intc
7、){intd;...}其栈结构为0x1ffc-->d0x2000-->a0x2004-->b0x2008-->c对于任何编译器,每个栈单元的大小都是sizeof(int),而函数的每个参数都至少要占一个栈单元大小,如函数voidfun1(chara,intb,doublec,shortd)对一个32的系统其栈的结构就是0x1ffc-->a(4字节)0x2000-->b(4字节)0x2004-->c(8字节)0x200c-->d(4字节)对于函数voidfu