va_arg,va_start,va_end 使用學習

對於 可變參數的使用,腳本語言python,ruby使用起來很方便 不過C、C++也有利器,那就是va_arg之類函數。

筆者每次使用的時候,都要查msdn的文檔, 所以這次把它記錄下來。 方便以後的查詢。


首先是對可變參數處理的函數有哪些,需要哪些頭文件。



其次, 各個函數分別是幹什麼的

void test(int i,....)
{
   va_list vPtr; //申明一個可變的變量
   var_start(vPtr,i); //將 vPtr指向可變變量的第一個變量
   
   int n = va_arg(vPtr,int);//獲得當時vPtr位置的變量,並將vPtr批向下一個位置。 這裏下一個位置是由va_arg的第二個參數決定的。 
   va_end(vPtr);//在所有的變量處理完之後,將vPtr重置爲NULL
}


從這個函數的實現可以看到,我們使用可變參數應該有以下步驟: 
1)首先在函數裏定義一個va_list型的變量,這裏是arg_ptr,這個變 
量是指向參數的指針. 
2)然後用va_start宏初始化變量arg_ptr,這個宏的第二個參數是第 
一個可變參數的前一個參數,是一個固定的參數. 
3)然後用va_arg返回可變的參數,並賦值給整數j. va_arg的第二個 
參數是你要返回的參數的類型,這裏是int型. 
4)最後用va_end宏結束可變參數的獲取.然後你就可以在函數裏使 
用第二個參數了.如果函數有多個可變參數的,依次調用va_arg獲 
取各個參數.

再次, 我們來看看可變參數在編譯器中的處理

va_start, va_arg 和va_end是在stdarg.h中定義的, 由於硬件平臺的不同與編譯器的不同,所以定義的宏也有所不同,

以下,筆者會以VS2005中的定義爲例。

在stdarg.h中,實際上再次引用vadefs.h中對他們的定義。


在vadef.h中, 對他們的定義如下

typedef char *  va_list;
#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )

定義_INITSIZEOF(n)主要是爲了某些爲了需要內在的對齊的系統。

函數是從右向左壓棧的, 下圖畫出了函數的參數在堆棧中的位置。


我們看到ADDRESSOF(v)是把v強制轉換爲char

_crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) 是獲得當前固定參數的位置再加上參數所佔的大小, 就指向了第一個可變變量的位置。

然後就是用va_arg來取得這個參數。

_crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

首先是ap+=sizeof(t),這裏ap已經指向下一個參數的位置, 然後返回 ap-sizeof(t),這個正是第一個可變參數在堆棧中的位置。 再用*取得這個地址的內容。


最後是va_end,  _crt_va_end(ap)      ( ap = (char *)0 )這樣使得ap不再指向堆棧.


最後,

由於是定義的宏, 所以 可變參數的個數, 參數的類型完全是由使用者來定義的, 它並不能智能的識別不同的參數的個數與類型。 

所以筆者在使用的過程中, 是模仿printf來寫的, 就是在前面指定format的類型。 再用相應的類型來解析。

case 's': va_arg(arg, const char*); break;
case 'i': va_arg(arg, int);         break;
case 't': va_arg(arg, time_t);      break;
case 'D': &va_arg(arg, time_t);      break;
case 'T': va_arg(arg, time_t);      break;
case 'f': va_arg(arg, double);      break;


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