可变参数的函数
2015-06-27 11:39
393 查看
转载自:
/article/6957896.html
http://www.cnblogs.com/MarvinGeng/archive/2012/07/19/2598923.html
感谢秋水LeoChin,MarvinGeng的详细说明!
1. 函数声明
首先,要实现类似printf()的变参函数,函数的最后一个参数要用 ... 表示,如
int log(char * arg1, ...)
这样编译器才能知道这个函数是变参函数。这个参数与变参函数的内部实现完全没有关系,只是让编译器在编译调用此类函数的语句时不计较参数多少老老实实地把全部参数压栈而不报错,当然...之前至少要有一个普通的参数,这是由实现手段限制的。
2. 函数实现
C语言通过几个宏实现变参的寻址。下面是linux2.18内核源码里这几个宏的定义,相信符合C89,C99标准的C语言基本都是这样定义的。
View Code
4. 总结:
1)标准C库的中的三个宏的作用只是用来确定可变参数列表中每个参数的内存地址,编译器是不知道参数的实际数目的。 在实际应用的代码中,程序员必须自己考虑确定参数数目的办法,如 :
①在固定参数中设标志-- printf函数就是用这个办法。
②在预先设定一个特殊的结束标记,就是说多输入一个可变参数,调用时要将最后一个可变参数的值设置成这个特殊的值,在函数体中根据这个值判断是否达到参数的结尾。本文前面的代码就是采用这个办法.
无论采用哪种办法,程序员都应该在文档中告诉调用者自己的约定。
2)实现可变参数的要点就是想办法取得每个参数的地址,取得地址的办法由以下几个因素决定:
①函数栈的生长方向
②参数的入栈顺序
③CPU的对齐方式
④内存地址的表达方式
结合源代码,我们可以看出va_list的实现是由④决定的,_INTSIZEOF(n)的引入则是由③决定的,他和①②又一起决定了va_start的实现,最后va_end的存在则是良好编程风格的体现,将不再使用的指针设为NULL,这样可以防止以后的误操作。
/article/6957896.html
http://www.cnblogs.com/MarvinGeng/archive/2012/07/19/2598923.html
感谢秋水LeoChin,MarvinGeng的详细说明!
1. 函数声明
首先,要实现类似printf()的变参函数,函数的最后一个参数要用 ... 表示,如
int log(char * arg1, ...)
这样编译器才能知道这个函数是变参函数。这个参数与变参函数的内部实现完全没有关系,只是让编译器在编译调用此类函数的语句时不计较参数多少老老实实地把全部参数压栈而不报错,当然...之前至少要有一个普通的参数,这是由实现手段限制的。
2. 函数实现
C语言通过几个宏实现变参的寻址。下面是linux2.18内核源码里这几个宏的定义,相信符合C89,C99标准的C语言基本都是这样定义的。
int log(char * fmt,...){ va_list ap; int d; char c, *p, *s; va_start(ap, fmt); while (*fmt) switch(*fmt++) { case 's': /* string */ s = va_arg(ap, char *); printf("string %s/n", s); break; case 'd': /* int */ d = va_arg(ap, int); printf("int %d/n", d); break; case 'c': /* char */ c = va_arg(ap, char); printf("char %c/n", c); break; } va_end(ap); }
View Code
4. 总结:
1)标准C库的中的三个宏的作用只是用来确定可变参数列表中每个参数的内存地址,编译器是不知道参数的实际数目的。 在实际应用的代码中,程序员必须自己考虑确定参数数目的办法,如 :
①在固定参数中设标志-- printf函数就是用这个办法。
②在预先设定一个特殊的结束标记,就是说多输入一个可变参数,调用时要将最后一个可变参数的值设置成这个特殊的值,在函数体中根据这个值判断是否达到参数的结尾。本文前面的代码就是采用这个办法.
无论采用哪种办法,程序员都应该在文档中告诉调用者自己的约定。
2)实现可变参数的要点就是想办法取得每个参数的地址,取得地址的办法由以下几个因素决定:
①函数栈的生长方向
②参数的入栈顺序
③CPU的对齐方式
④内存地址的表达方式
结合源代码,我们可以看出va_list的实现是由④决定的,_INTSIZEOF(n)的引入则是由③决定的,他和①②又一起决定了va_start的实现,最后va_end的存在则是良好编程风格的体现,将不再使用的指针设为NULL,这样可以防止以后的误操作。
相关文章推荐
- 在二维数组中查找有无此数(非重新排序)
- html中必备标签及引用W3C标准
- CF 552C 进制转换
- 用L脚本语言实现"L脚本语言控制台"
- 高级指针
- 关于即时通讯系统中消息发送、转发、展示、提示等专利初探
- 网页引用外部js脚本文件时必须是(转)
- OpenStack概念
- 6.27
- Unity3D资源存放笔记
- Eclipse debug 断点不能调试 ,Eclipse Unable to install breakpoint in 解决办法
- Miss,Ms. Mrs. Mr. Dr.区别
- L脚本语言实现文件加解密
- Ubuntu下面安装eclipse for c++
- Android开发中 软件盘将布局遮住的问题
- 计数排序(C语言版本)
- qca4004模块离线不上线原因分析
- Dynamics AX 2012 R2 在AIF服务契约中使用DateTime
- JavaWeb学习笔记——访问静态HTML网页
- 手把手教你使用CocoaPods管理你的iOS第三方开源类库