C语言中的不少函数的入参被定义为可变参数,最典型的函数
int printf (const char * fmt, ...)spa
要对其中的可变参数进行处理,就要用到va_list类型和 VA_START, VA_END, VA_ARG 宏 ,须要包含<stdarg.h>头文件设计
利用va族函数对不定参数进行解析的过程所示以下:指针
1 int my_printf(const char * fmt, ...) 2 { 3 va_list struAp; 4 va_start(struAp, fmt); 5 6 for (; *fmt; ++fmt) 7 { 8 if(*fmt != '%') 9 { 10 PUTC(*fmt); 11 continue; 12 } 13 14 fmt++; 15 16 switch (*fmt) 17 { 18 case 'd': 19 { 20 int i = va_arg(struAp,int); 21 PUTC(i); 22 } 23 break; 24 25 default: 26 break; 27 } 28 } 29 30 va_end(struAp); 31 }
要了解不定参数的处理方式,就要搞清楚va族函数的实现code
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )blog
/* 实质就是一个char型的指针 */字符串
typedef char * va_list;原型
/* 将指针偏移一个v的长度,指向后面的地址 */it
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )io
/* 根据参数类型,取出后面的数据,强制类型转换 */
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
/* 将指针置为NULL */
#define va_end(ap) ( ap = (va_list)0 )
经过解析fmt字符串,获得后面的参数类型和个数,根据参数类型再加上偏移量就能够找到栈中的不定参数了
函数调用和传参的过程所示以下:
将函数参数与函数调用后下一条指令的地址都压入栈中,而后跳到函数的入口地址。
例如
void func(int param1, double param2,int param3){ } int main() { func(3, 1.2, 4); printf("Over\n"); //设指令地址为0x1234 return 0; }
执行f(3, 1.2, 4)的函数调用,进入func函数时的堆栈以下:
——float类型的实际参数将提高到double
——char类型的实际参数将提高到int
——short类型的实际参数将提高到int
——《C语言程序设计》第2版 2.7 类型转换 p36
c = va_arg(ap,char);
由于咱们没法传递一个char类型参数,若是传递了,它将会被自动转化为int类型。上面的式子应该写成: