详解 C语言可变参数 va_list和_vsnprintfprintf实现

详解 C语言可变参数 va_list和_vsnprintfprintf实现

ID:47813917

大小:61.50 KB

页数:8页

时间:2019-11-16

详解 C语言可变参数 va_list和_vsnprintfprintf实现_第1页
详解 C语言可变参数 va_list和_vsnprintfprintf实现_第2页
详解 C语言可变参数 va_list和_vsnprintfprintf实现_第3页
详解 C语言可变参数 va_list和_vsnprintfprintf实现_第4页
详解 C语言可变参数 va_list和_vsnprintfprintf实现_第5页
资源描述:

《详解 C语言可变参数 va_list和_vsnprintfprintf实现》由会员上传分享,免费在线阅读,更多相关内容在学术论文-天天文库

1、-C语言的变长参数在平时做开发时很少会在自己设计的接口中用到,但我们最常用的接口printf就是使用的变长参数接口,在感受到printf强大的魅力的同时,是否想挖据一下到底printf是如何实现的呢?这里我们一起来挖掘一下C语言变长参数的奥秘。先考虑这样一个问题:如果我们不使用C标准库(libc)中提供的Facilities,我们自己是否可以实现拥有变长参数的函数呢?我们不妨试试。一步一步进入正题,我们先看看固定参数列表函数,voidfixed_args_func(inta,doubleb,char*c){       printf("a=0x%p",&a);       pr

2、intf("b=0x%p",&b);       printf("c=0x%p",&c);}对于固定参数列表的函数,每个参数的名称、类型都是直接可见的,他们的地址也都是可以直接得到的,比如:通过&a我们可以得到a的地址,并通过函数原型声明了解到a是int类型的;通过&b我们可以得到b的地址,并通过函数原型声明了解到b是double类型的;通过&c我们可以得到c的地址,并通过函数原型声明了解到c是char*类型的。但是对于变长参数的函数,我们就没有这么顺利了。还好,按照C标准的说明,支持变长参数的函数在原型声明中,必须有至少一个最左固定参数(这一点与传统C有区别,传统C允许不

3、带任何固定参数的纯变长参数函数),这样我们可以得到其中固定参数的地址,但是依然无法从声明中得到其他变长参数的地址,比如:voidvar_args_func(constchar*fmt,...){   ......}这里我们只能得到fmt这固定参数的地址,仅从函数原型我们是无法确定"..."中有几个参数、参数都是什么类型的,自然也就无法确定其位置了。那么如何可以做到呢?在大脑中回想一下函数传参的过程,无论"..."中有多少个参数、每个参数是什么类型的,它们都和固定参数的传参过程是一样的,简单来讲都是栈操作,而栈这个东西对我们是开放的。这样一来,一旦我们知道某函数帧的栈上的一个固定参数

4、的位置,我们完全有可能推导出其他变长参数的位置,顺着这个思路,我们继续往下走,通过一个例子来诠释一下:(这里要说明的是:函数参数进栈以及参数空间地址分配都是"实现相关"的,不同平台、不同编译器都可能不同,所以下面的例子仅在IA-32,WindowsXP,MinGWgccv3.4.2下成立)我们先用上面的那个fixed_args_func函数确定一下这个平台下的入栈顺序。intmain(){   fixed_args_func(17,5.40,"helloworld");   return0;}a=0x0022FF50b=.---0x0022FF54c=0x0022FF5C从这个结果

5、来看,显然参数是从右到左,逐一压入栈中的(栈的延伸方向是从高地址到低地址,栈底的占领着最高内存地址,先入栈的参数,其地理位置也就最高了)。我们基本可以得出这样一个结论: c.addr=b.addr+x_sizeof(b); /*注意: x_sizeof!=sizeof,后话再说*/ b.addr=a.addr+x_sizeof(a);有了以上的"等式",我们似乎可以推导出voidvar_args_func(constchar*fmt,...)函数中,可变参数的位置了。起码第一个可变参数的位置应该是:first_vararg.addr=fmt.addr+x_sizeof(fmt); 

6、根据这一结论我们试着实现一个支持可变参数的函数:voidvar_args_func(constchar*fmt,...){   char   *ap;   ap=((char*)&fmt)+sizeof(fmt);   printf("%d",*(int*)ap);          ap= ap+sizeof(int);   printf("%d",*(int*)ap);   ap= ap+sizeof(int);   printf("%s",*((char**)ap));}intmain(){   var_args_func("%d%d%s",4,5,"hell

7、oworld");}输出结果:45helloworldvar_args_func只是为了演示,并未根据fmt消息中的格式字符串来判断变参的个数和类型,而是直接在实现中写死了,如果你把这个程序拿到solaris9下,运行后,一定得不到正确的结果,为什么呢,后续再说。先来解释一下这个程序。我们用ap获取第一个变参的地址,我们知道第一个变参是4,一个int型,所以我们用(int*)ap以告诉编译器,以ap为首地址的那块内存我们要将之视为一个整型来使用,*(int*)ap获得

当前文档最多预览五页,下载文档查看全文

此文档下载收益归作者所有

当前文档最多预览五页,下载文档查看全文
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,天天文库负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。