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

我们都很熟悉,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
地址,不可访问,强行取值会段错误,这样就避免了野指针的产生.

你可能感兴趣的:(杂七杂八)