一. printf函数的实现原理
1.从后向前扫描,C/C++的函数参数是通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出来。
2.栈是从内存的高地址向低地址生长的,控制生长的就是堆栈指针了,最先压入的参数是在最上面,就是说在所有参数的最后面,最后压入的参数在最下面,结构上看起来是第一个,所以最后压入的参数总是能够被函数找到,因为它就在堆栈指针的上方。
所以printf首先找到的就是字符指针”%d%s”,比如,printf(“%d%s”,10,”sacd”);
二、可变参数的设计
标准头文件
<stdarg.h>
定义了数据类型va_list,三个宏:va_start, va_arg, va_endva-list是一个char类型的指针,当被调用函数使用一个可变参数时,它声明一个类型为va-list的变量,该变量用来指向va-arg和va-end所需信息的位置
typedef char * va_list;
void va_start(va-list ap,lastfix);
ap:指向被传递给函数的可变参数表中的第一个参数,可以由它得到第一个参数内存的地址;
astfix:是传递给被调用函数的最后一个固定参数的标识符
- va_start在C中的源码
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //得到可变参数中第一个参数的首地址
type va_arg(va_list ap,type)也是一个宏,返回ap所指对象的值,修改参数指针ap使其增加以指向表中下一个参数;
va_arg的第二个参数提供了修改参数指针所必需的信息。在第一次使用va-arg时,它返回可变参数表中的第一个参数,后续的调用都返回表中的下一个参数,下面给出va_arg在C中的源码
#define va_arg(ap,type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) ) //将参数转换成需要的类型,并使ap指向下一个参数
注意第二个参数所用类型名应与传递到堆栈的参数的字节数对应,以保证能对不同类型的可变参数进行正确地寻址,比如实参依次为char型、char * 型、int型和float型时,在va-arg中它们的类型则应分别为int、char *、int和double.
#define va_end(ap) ( ap = (va_list)0 )
举例说明:比如我们要求可变参数的和 int sum(int n,…)
1.首先要用宏对变量n进行初始化: va_start(vap,n);相当于 char vap = (char )&n + sizeof(int); 此时vap就指向n后面可变参数中的第一个参数
2.通过va_arg访问函数调用的各个实际参数,宏va_arg的类型特征为 va_arg(va_list vap, 类型名) va_arg不仅返回一个实际参数的值,完成某种更新操作,指向下一个参数的地址 v = va_arg(vap, int);
3.函数在退出之前,必须有一个结束动作。va_end void va_end(va_list vap);
参考资料: http://blog.csdn.net/hackbuteer1/article/details/7558979