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類型。上面的式子應該寫成: