C语言中变参函数的实现细节_第1页
C语言中变参函数的实现细节_第2页
C语言中变参函数的实现细节_第3页
C语言中变参函数的实现细节_第4页
C语言中变参函数的实现细节_第5页
全文预览已结束

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

本文格式为Word版,下载可任意编辑——C语言中变参函数的实现细节C语言中变参函数的实现细节

C语言的函数虽然不具备C++的多态性,但也可以采纳参数不确定的处境,当然,C语言中的变参函数实际在功能上是受限的,废话不多讲,下面来看看变参函数的边边角角的问题。以下仅供参考!

议论之前我们来看一下最熟谙的变参函数printf的原型声明:

1intprintfconstchar*format,...;

留神到,在函数中声明其参数是可变的方法是三个点“...”,但同时,这个函数务必要有一个固定的参数,譬如printf里面的这个format,也就是说变参函数的参数数目至少是一个。这是由C语言中实现变参的原理计算堆栈地址抉择的。顺着printf函数我们来看看它的定义是什么:

1234567891011121314151617int__printfconstchar*format,...va_listarg;intdone;va_startarg,format;done=vfprintfstdout,format,arg;va_endarg;returndone;

留神到库函数中内部定义的变量和函数用了双下划线开头,这也是我们写应用程序时尽量不要用双下划线开头的理由,我们也不理应使用单下划线开头的函数和变量,由于那也是系统留存的

其中察觉__printf函数里用了va_list,va_start,va_end等宏,事实上,在__printf中调用的vfpirntf函数还用到了一个叫做va_arg的宏,这几个宏就是编写变参函数的关键。现在我们自己写一个最简朴的变参函数,先来个感性熟悉:

123456789101112131415161718192021222324252627282930313233#include#includevoidsimple_va_funinti,...va_listarg_ptr;//定义一个用来指向函数变参列表的指针arg_ptrintj;va_startarg_ptr,i;//使arg_ptr指向第一个可变参数j=va_argarg_ptr,int;//取得arg_ptr当前所指向的参数的值,并使arg_ptr指向下一个参数va_endarg_ptr;//指示提取参数终止printf%d%d,i,j;return;intmainvoidsimple_va_fun3,4;return0;

如代码中的解释所示,arg_ptr实际上是一个指向函数变参列表的指针,va_list实际上是void指针类型。

va_start用来初始化这个指针,使之指向变参列表中的第一个参数,留神到它的其次个参数是变参函数的那个固定参数。

va_arg利用已经初始化了的arg_ptr指针来取得变参列表中各个参数的值,第一个参数是变参列表指针,其次个参数是当前参数的类型。

va_end宏用来提示终止参数终止,在LINUX的glibc实现中,va_end实际上就是一个空语句void0

各个宏定义在头文件stdarg.h中声明,因此我们需要包含这个头文件。其概括的定义如下:

1234567891011#define_AUPBNDsizeofacpi_native_int-1#define_ADNBNDsizeofacpi_native_int-1#define_bndX,bndsizeofX+bnd~bnd#defineva_startap,Avoidap=char*A+_bndA,_AUPBND#defindva_argap,T*T*ap+=_bndT,_AUPBND-_bndT,_ADNBDN#defineva_endapvoid0

这些宏定义都对比繁琐,主要目的是为了适应不同系统的地址对齐问题。

上面说过,va_start的功能实际上是使ap指针指向第一个变参,A就是我们的第一个固定参数,不考虑地址对齐,最简朴的手段当然如下:

ap=A+sizeofA

上述代码其实也是实现的这个简朴的功能,但经过宏_AUPBND和_bnd之后,就能保证ap指向的地址至少是关于acpi_native_int对齐的,打个比方,假设此时A的地址是0x0003,而且A的类型占用4个字节,而当前系统要求4个字节对齐,那么就让_AUPBND中的sizeof参数为4,经过屡屡宏替代之后ap的地址值就会是0x0008,而简朴地用上面的算式ap=A+sizeofA计算出的结果是0x0007。

同样地,va_arg宏替代在不考虑任何移植性问题时,要取得当前变参的值并使指针指向下一个参数最简朴的手段如下:

*ap+=sizeofT-sizeofT

这个需要稍微解释一下,首先,C里面的.参数压栈是从右到左依次压栈的,因此可以想象,第一个固定参数在栈顶LINUX进程映像中栈是倒着增长的,这个地址是全体参数中最小的,其次个参数也就是第一个变参在紧接着固定参数之上,以此类推。因此,要想ap指针不断指向下一个参数,就务必让它每次都加上当前指向的变量所占内存的大小即ap+=sizeofT的含义。

接下来,利用这个地址值又减去sizeofT,实际上地址值又回到上一个参数处留神,此时ap指针的值并未变更,也就是说,va_arg宏实现获取第一个变参的值的时候是先使ap指向其次个变参,然后再去获取第一个变参的值,然后取值。

va_end宏就对比简朴了,虽然各种平台的实现细节不一样,但是道理都是一样的,在glibc中va_end被简朴地实现为一个空语句。

由此可见,实际上C语言的所谓变参函数是很笨的,它根本上啥智能都没有,不能跟C++的多态性和符号重载相比,我们在传递参数的时候虽然可以传递不定个数的参数,但是这些参数都务必在函数实现中赋予一一处理。所以我还是对比推崇C++呵呵!

至于printf这个淘气鬼,上面

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论