c語言中可變參數的原理---printf

一.   何謂可變參數
int   printf(   const   char*   format,   ...);  
這是使用過C語言的人所再熟悉不過的printf函數原型,它的參數中就有固定參數format和可變參數(用”…”表示).   而我們又可以用各種方式來調用printf,如:
printf( "%d ",value);  
printf( "%s ",str);  
printf( "the   number   is   %d   ,string   is:%s ",   value,   str);

二.實現原理
C語言用宏來處理這些可變參數。這些宏看起來很複雜,其實原理挺簡單,就是根據參數入棧的特點從最靠近第一個可變參數的固定參數開始,依次獲取每個可變參數的地址。下面我們來分析這些宏。在VC中的stdarg.h頭文件中,針對不同平臺有不同的宏定義,我們選取X86平臺下的宏定義:
typedef   char   *va_list;  
/*把va_list被定義成char*,這是因爲在我們目前所用的PC機上,字符指針類型可以用來存儲內存單元地址。而在有的機器上va_list是被定義成void*的*/
#define   _INTSIZEOF(n)   (   (sizeof(n)   +   sizeof(int)   -   1)   &   ~(sizeof(int)   -   1)   )
/*_INTSIZEOF(n)宏是爲了考慮那些內存地址需要對齊的系統,從宏的名字來應該是跟sizeof(int)對齊。一般的 sizeof(int)=4,也就是參數在內存中的地址都爲4的倍數。比如,如果sizeof(n)在1-4之間,那麼_INTSIZEOF(n)=4;如果sizeof(n)在5-8之間,那麼_INTSIZEOF(n)=8。*/
#define   va_start(ap,v)(   ap   =   (va_list)&v   +   _INTSIZEOF(v)   )
/*va_start的定義爲   &v+_INTSIZEOF(v)   ,這裏&v是最後一個固定參數的起始地址,再加上其實際佔用大小後,就得到了第一個可變參數的起始內存地址。所以我們運行 va_start(ap,   v)以後,ap指向第一個可變參數在的內存地址*/
#define   va_arg(ap,t)   (   *(t   *)((ap   +=   _INTSIZEOF(t))   -   _INTSIZEOF(t))   )
/*這個宏做了兩個事情,
①用用戶輸入的類型名對參數地址進行強制類型轉換,得到用戶所需要的值
②計算出本參數的實際大小,將指針調到本參數的結尾,也就是下一個參數的首地址,以便後續處理。*/
#define   va_end(ap)   (   ap   =   (va_list)0   )  
/*x86平臺定義爲ap=(char*)0;使ap不再   指向堆棧,而是跟NULL一樣.有些直接定義爲((void*)0),這樣編譯器不會爲va_end產生代碼,例如gcc在linux的x86平臺就是這樣定義的.   在這裏大家要注意一個問題:由於參數的地址用於va_start宏,所以參數不能聲明爲寄存器變量或作爲函數或數組類型.   */
以下再用圖來表示:
在VC等絕大多數C編譯器中,默認情況下,參數進棧的順序是由右向左的,因此,參數進棧以後的內存模型如下圖所示:最後一個固定參數的地址位於第一個可變參數之下,並且是連續存儲的。
|——————————————————————————|
|最後一個可變參數   |   -> 高內存地址處
|——————————————————————————|
...................
|——————————————————————————|
|第N個可變參數   |   -> va_arg(arg_ptr,int)後arg_ptr所指的地方,
|   |   即第N個可變參數的地址。
|———————————————   |  
………………………….
|——————————————————————————|
|第一個可變參數   |   -> va_start(arg_ptr,start)後arg_ptr所指的地方
|   |   即第一個可變參數的地址
|———————————————   |  
|————————————————————————   ——|
|   |
|最後一個固定參數   |   ->   start的起始地址
|——————————————   —|   .................
|——————————————————————————   |
|   |
|———————————————   |->   低內存地址處

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章