可變引數的乙個前提:_cdecl (引數壓棧方向:從右向左)
可變引數巨集:
va_start,va_arg,va_end是在stdarg.h中被定義成巨集的,由於硬體平台的不同,編譯器的不同,所以定義的巨集也有所不同,下面以vc++中stdarg.h裡x86平台的巨集定義摘錄如下:
typedef char * va_list; #define _intsizeof(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v + _intsizeof(v) ) #define va_arg(ap,t) ( *(t *)((ap += _intsizeof(t)) - _intsizeof(t)) ) #define va_end(ap) ( ap = (va_list)0 )
定義_intsizeof(n)主要是為了記憶體對齊,c語言的函式是從右向左壓入堆疊的(設資料進棧方向為從高位址向低位址發展,即首先壓入的資料在高位址). 下圖是函式的引數在堆疊中的分布位置:
低位址 |-----------------------------|<-- &v
|第n-1個引數(最後乙個固定引數)|
|-----------------------------|<--va_start後ap指向
|第n個引數(第乙個可變引數) |
|-----------------------------|
|....... |
|-----------------------------|
|函式返回位址 |
高位址 |-----------------------------|
1. va_list 被定義為char *
2. va_start 將位址ap定義為 &v+_intsizeof(v),而&v是固定引數在堆疊的位址,所以va_start(ap, v)以後,ap指向第乙個可變引數在堆疊的位址
3. va_arg 取得型別t的可變引數值,以int型為例,va_arg取int型的返回值:
j= ( *(int*)((ap += _intsizeof(int))-_intsizeof(int)) );
4. va_end 使ap不再指向堆疊,而是跟null一樣.這樣編譯器不會為va_end產生**.
示例:
void ******_va_fun(int i, ...)
從這個函式的實現可以看到,使用可變引數應該有以下步驟:
1)首先在函式裡定義乙個va_list型的變數,這裡是arg_ptr,這個變數是指向引數的指標.
2)然後用va_start巨集初始化變數arg_ptr,這個巨集的第二個引數是第乙個可變引數的前乙個引數,是乙個固定的引數.
3)然後用va_arg返回可變的引數,並賦值給整數j. va_arg的第二個引數是你要返回的引數的型別,這裡是int型.
4)最後用va_end巨集結束可變引數的獲取.然後你就可以在函式裡使用第二個引數了.如果函式有多個可變引數的,依次呼叫va_arg獲取各個引數.
小結:
缺點:1、可變引數的型別和個數需要在該函式中由程式**控制:
2、編譯器對可變引數的函式的原型檢查不夠嚴格,對程式設計查錯不利;
因此:.如果在c++裡,應該利用c++的多型性來實現可變引數的功能,盡量避免用c語言的方式來實現.
c 中可變引數
在c 程式設計中,有時我們需要編寫一些在源 編寫階段無法確定引數個數,有時甚至無法確定引數型別的函式。例如,乙個求和函式。可以通過過載實現若干個數的和。int sum int i1,int i2 intsum int i1,int i2,int i3 還可以過載更多類似函式 double sum d...
c 中可變引數
在c 程式設計中,有時我們需要編寫一些在源 編寫階段無法確定引數個數,有時甚至無法確定引數型別的函式。例如,乙個求和函式。可以通過過載實現若干個數的和。int sum int i1,int i2 intsum int i1,int i2,int i3 還可以過載更多類似函式 double sum d...
c 中可變引數
在c 程式設計中,有時我們需要編寫一些在源 編寫階段無法確定引數個數,有時甚至無法確定引數型別的函式。例如,乙個求和函式。可以通過過載實現若干個數的和。int sum int i1,int i2 intsum int i1,int i2,int i3 還可以過載更多類似函式 double sum d...