- 1、本文档共8页,可阅读全部内容。
- 2、有哪些信誉好的足球投注网站(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
- 3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 4、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
- 5、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
- 6、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们。
- 7、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
- 8、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
详解C语言可变参数valist和vsnprintf及printf实现
C语言的变长参数在平时做开发时很少会在自己设计的接口中用到,但我们最常用的接口printf就是使用的变长参数接口,在感受到printf强大的魅力的同时,是否想挖据一下到底printf是如何实现的呢?这里我们一起来挖掘一下C语言变长参数的奥秘。先考虑这样一个问题:如果我们不使用C标准库(libc)中提供的Facilities,我们自己是否可以实现拥有变长参数的函数呢?我们不妨试试。一步一步进入正题,我们先看看固定参数列表函数,void fixed_args_func(int a, double b, char *c)
{??????? printf(a = 0x%p\n, a);??????? printf(b = 0x%p\n, b);??????? printf(c = 0x%p\n, c);}对于固定参数列表的函数,每个参数的名称、类型都是直接可见的,他们的地址也都是可以直接得到的,比如:通过a我们可以得到a的地址,并通过函数原型声明了解到a是int类型的; 通过b我们可以得到b的地址,并通过函数原型声明了解到b是double类型的; 通过c我们可以得到c的地址,并通过函数原型声明了解到c是char*类型的。但是对于变长参数的函数,我们就没有这么顺利了。还好,按照C标准的说明,支持变长参数的函数在原型声明中,必须有至少一个最左固定参数(这一点与传统C有区别,传统C允许不带任何固定参数的纯变长参数函数),这样我们可以得到其中固定参数的地址,但是依然无法从声明中得到其他变长参数的地址,比如:void var_args_func(const char * fmt, ... )
{?? ?... ... }这里我们只能得到fmt这固定参数的地址,仅从函数原型我们是无法确定...中有几个参数、参数都是什么类型的,自然也就无法确定其位置了。那么如何可以做到呢?在大脑中回想一下函数传参的过程,无论...中有多少个参数、每个参数是什么类型的,它们都和固定参数的传参过程是一样的,简单来讲都是 HYPERLINK /logs/1592114.html \t _blank 栈操作,而栈这个东西对我们是开放的。这样一来,一旦我们知道某函数帧的栈上的一个固定参数的位置,我们完全有可能推导出其他变长参数的位置,顺着这个思路,我们继续往下走,通??一个例子来诠释一下:(这里要说明的是:函数参数进栈以及参数空间地址分配都是实现相关的,不同平台、不同编译器都可能不同,所以下面的例子仅在IA-32,Windows XP, MinGW gcc v3.4.2下成立)我们先用上面的那个fixed_args_func函数确定一下这个平台下的入栈顺序。int main()
{?? ?fixed_args_func(17, 5.40, hello world);?? ?return 0;}a = 0x0022FF50b = 0x0022FF54c = 0x0022FF5C从这个结果来看,显然参数是从右到左,逐一压入栈中的(栈的延伸方向是从高地址到低地址,栈底的占领着最高内存地址,先入栈的参数,其地理位置也就最高了)。
我们基本可以得出这样一个结论:?c.addr = b.addr + x_sizeof(b);? /*注意:? x_sizeof != sizeof,后话再说 */?b.addr = a.addr + x_sizeof(a);有了以上的等式,我们似乎可以推导出 void var_args_func(const char * fmt, ... ) 函数中,可变参数的位置了。起码第一个可变参数的位置应该是:first_vararg.addr = fmt.addr + x_sizeof(fmt);? 根据这一结论我们试着实现一个支持可变参数的函数:void var_args_func(const char * fmt, ... )
{?? ?char?? ?*ap;?? ?ap = ((char*)fmt) + sizeof(fmt);?? ?printf(%d\n, *(int*)ap); ??? ??? ??? ?ap =? ap + sizeof(int);?? ?printf(%d\n, *(int*)ap);?? ?ap =? ap + sizeof(int);?? ?printf(%s\n, *((char**)ap));}int main()
{?? ?var_args_func(%d %d %s\n, 4, 5, hello world);}输出结果:45hello worldvar_args_func只是为了演示,并未根据fm
文档评论(0)