這幾個函數和變量是針對可變參數函數的,什麼是可變參數函數呢,最經典的莫過於printf和scanf,這兩個函數的聲明如下:
1 int printf(const char *format, ...); 2 3 int scanf(const char *format, ...);
這兩個函數聲明中省略號(...)表示的就是任意個數的參數,可變參數函數就是輸入的參數的個數是可變的,那麼這個具體是怎麼實現的呢?
要了解這個是怎麼實現,首先我們就要先理解一點,參數是如何傳遞給函數的。衆所周知,函數的數據是存放於棧中的,那麼給一個函數傳遞傳遞參數的過程就是將函數的參數從右向左逐次壓棧,例如:
func(int i, char c, doube d)
這個函數傳遞參數的過程就是將d,c,i逐次壓到函數的棧中,由於棧是從高地址向低地址擴展的,所以d的地址最高,i的地址最低。
理解了函數傳遞參數的過程,再來說一下va_list的原理,通常,可變參數的代碼是這麼寫的:
1 void func(char *fmt, ...) 2 { 3 va_list ap; 4 5 va_start(ap, fmt); 6 va_arg(ap, int); 7 va_end(va); 8 }
這裏ap其實就是一個指針,指向了參數的地址。
va_start()所做的就是讓ap指向函數的最後一個確定的參數(聲明程序中是fmt)的下一個參數的地址。
va_arg()所做的就是根據ap指向的地址,和第二個參數所確定的類型,將這個參數的中的數據提取出來,作爲返回值,同時讓ap指向下一個參數。
va_end()所做的就是讓ap這個指針指向0。
關於這三個參數實現的宏可以參看下面的實現:
1 // 使ap指向第一個可變參數的地址 2 #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) 3 4 // 使ap指向下一個可變參數,同時將目前ap所指向的參數提取出來並返回 5 #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) 6 7 // 銷燬ap 8 #define va_end(ap) ( ap = (va_list)0 )