关于printf()中可变参数的实现的理解
2016-04-09 18:41
399 查看
我们都很熟悉,printf()函数里面可以实现自定义参数的个数和类型,那么底层到底是怎么实现的呢?
参考网上一个简易的printf的实现
在stdarg.h里面能发现这些:
现在分析 #define _INTSIZEOF(n) \ ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) ,
假设这里的n是char.
sizeof(char)是1;
sizeof(int)是4;
==>((sizeof(n)+sizeof(int)-1)是4,化为二进制是00000100B
假设cpu为32位.(sizeof(int)-1)是3,化为二进制是00000011B,取反得到11111100B,
相与得到00000100B,即是4.
假设这里的n是short,按照上面的做法得到00000100B,即是4.
假设这里的n是int,按照上面的做法得到00000100B,即是4.
假设这里的n是double,按照上面的做法得到000001000B,即是8.
这个宏的功能就是以4为单位进行向下对齐,比如0 1 2 3都得到0, 4 5 6 7 都得到4,这个功能是
为了数据在内存中进行对齐,方便cpu进行取数据用的.
然后分析#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ):
常用的printf形如(“number is %d,%c”,a,’b’), 在上面函数的第10行可以看到 va_start (ap, fmt),
这里的fmt就是”numbei is %d”的首地址,一个4字节指针.ap就指向它. 经过处理已经开始
执行到case ‘d’: ival = va_arg(ap, int);替换后得到
( (int )((ap += _INTSIZEOF(int)) - _INTSIZEOF(int)) ), 这时ap指向了第2个可变参数即’b’.
但是通过- _INTSIZEOF(int)返回的是变量a的地址,转化的(int*)型指针,然后 * ( int * )取值返回给ival.
全部处理完后#define va_end(ap) ( ap = (va_list)0 ),意思就是把原来的ap重新指向0地址,因为在虚拟内存0
地址,不可访问,强行取值会段错误,这样就避免了野指针的产生.
参考网上一个简易的printf的实现
void minprintf(char *fmt, ...) 4 { 5 va_list ap; 6 char *p, *sval; 7 int ival; 8 double dval; 9 10 va_start(ap, fmt); 11 for (p = fmt; *p; p++) { 12 if(*p != '%') { 13 putchar(*p); 14 continue; 15 } 16 switch(*++p) { 17 case 'd': 18 ival = va_arg(ap, int); 19 printf("%d", ival); 20 break; 21 case 'f': 22 dval = va_arg(ap, double); 23 printf("%f", dval); 24 break; 25 case 's': 26 for (sval = va_arg(ap, char *); *sval; sval++) 27 putchar(*sval); 28 break; 29 default: 30 putchar(*p); 31 break; 32 } 33 } 34 va_end(ap); 35 }
在stdarg.h里面能发现这些:
typedef char * va_list; #define _INTSIZEOF(n) \ ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) #define va_arg(ap,t) \ ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 )
现在分析 #define _INTSIZEOF(n) \ ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) ,
假设这里的n是char.
sizeof(char)是1;
sizeof(int)是4;
==>((sizeof(n)+sizeof(int)-1)是4,化为二进制是00000100B
假设cpu为32位.(sizeof(int)-1)是3,化为二进制是00000011B,取反得到11111100B,
相与得到00000100B,即是4.
假设这里的n是short,按照上面的做法得到00000100B,即是4.
假设这里的n是int,按照上面的做法得到00000100B,即是4.
假设这里的n是double,按照上面的做法得到000001000B,即是8.
这个宏的功能就是以4为单位进行向下对齐,比如0 1 2 3都得到0, 4 5 6 7 都得到4,这个功能是
为了数据在内存中进行对齐,方便cpu进行取数据用的.
然后分析#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ):
常用的printf形如(“number is %d,%c”,a,’b’), 在上面函数的第10行可以看到 va_start (ap, fmt),
这里的fmt就是”numbei is %d”的首地址,一个4字节指针.ap就指向它. 经过处理已经开始
执行到case ‘d’: ival = va_arg(ap, int);替换后得到
( (int )((ap += _INTSIZEOF(int)) - _INTSIZEOF(int)) ), 这时ap指向了第2个可变参数即’b’.
但是通过- _INTSIZEOF(int)返回的是变量a的地址,转化的(int*)型指针,然后 * ( int * )取值返回给ival.
全部处理完后#define va_end(ap) ( ap = (va_list)0 ),意思就是把原来的ap重新指向0地址,因为在虚拟内存0
地址,不可访问,强行取值会段错误,这样就避免了野指针的产生.
相关文章推荐
- HTML写的第一个邮箱登陆的界面
- 《Linux操作系统分析》之分析Linux内核如何装载和启动一个可执行程序
- Android 开发 上传用户头像到服务器的数据库再获取显示
- [iOS]File does not exist
- 【勘误清单】《机器学习》 周志华 北京: 清华大学出版社
- Unity3D中使用Leap Motion进行手势控制
- 201509-5 最佳文章
- Android表格自定义控件
- RSA加解密算法java实现(已添加分段加密算法处理)
- Android 编码规范
- Android退出时创建弹窗对话框
- Clover KextsToPatch 使用方法 2015.10.21
- iOS runtime——函数/使用方法/使用场景/示例
- linux系统安装yum服务
- 简单的算法动手写备忘
- 第七课 可执行程序的装载(1)
- linux基础学习之 gSOAP文件夹中的README中文翻译
- STL的基本使用之关联容器:map和multiMap的基本使用
- 第一次结对项目报告
- git相关操作流程学习