您的位置:首页 > 其它

关于printf()中可变参数的实现的理解

2016-04-09 18:41 399 查看
我们都很熟悉,printf()函数里面可以实现自定义参数的个数和类型,那么底层到底是怎么实现的呢?

参考网上一个简易的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

地址,不可访问,强行取值会段错误,这样就避免了野指针的产生.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: