在採用c語言程式設計時,函式中的形式引數數目通常是確定的,在呼叫的時候要依次給出與形式引數對應的所有實際引數,但在某些情況下希望函式的引數個數可以根據需要確定,如printf,scanf函式等,c編譯器提供了一系列處理這種情況的巨集,以遮蔽不同的硬體平台造成的差異,增加程式的可移植性,這些巨集包括va_start,va_arg和va_end等。在採用ansi標準形式的時候,引數個數可變的函式原型宣告為:
type funcname(type para1,...);
這種形式至少需要乙個固定的形式引數,後面的省略號是函式原形的一部分,type是函式返回值和形式引數的型別;
若採用unix system v相容的宣告方式時,引數個數可變的函式原型為:
type funcname(va_list);va_dcl
這種形式不需要提供固定引數,type是函式返回值得型別,va_dcl是對函式原型宣告中的ca_alist的詳細宣告,實際是乙個巨集定義,對不同硬體平台採用不同的型別來定義,但在最後都包括了乙個分號,因此va_dcl後不需要再加分號了,va_dcl在**中必須原樣給出,va_alist在vc中可原樣給出也可以略去,但在unix上的cc或linux上的gcc中都要省略掉;
由於硬體平台的不同和編譯器的不同,所以定義的巨集也有所區別,下面看一下vc++6.0下這些巨集的定義以及使用和說明:vc下這些巨集的定義都在stdarg.h標頭檔案中
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_end(ap) (ap = (va_list))
va在這裡是
variable-argument(
可變引數
)的意思
.
(1)函式使用的時候會首先定義乙個va_list型的變數,這個變數是儲存引數位址的指標,因為得到引數位址之後,再結合引數型別,才能得到引數的值,va_list被定義為char*,因為目前我們使用的pc機上,字元指標型別用來儲存記憶體單元位址,有些機器上也會定義為void*型別;
(2)定義_intsizeof(n)主要是為了某些需要記憶體對齊的系統,這個巨集的目的就是為了得到最後乙個固定引數的實際記憶體大小;一般說來它和sizeof一樣;
(3)
va_start(va_list,type)的作用是指向第乙個可變引數,va_start的定義為&v + _intsizeof(v),這裡&v是最後乙個固定引數的起始位址,再加上其實際占用大小後,就得到了第乙個可變引數的起始記憶體位址;
(4)va_arg(va_list,type)的作用是根據指定的的引數型別取得本引數的值,並使va_list指向像乙個引數的起始位址,相當於進棧操作,
#define va_arg(ap,t) ( *(t *)((ap += _intsizeof(t)) - _intsizeof(t)) )
這個巨集做了兩個事情,
①用使用者輸入的型別名對引數位址進行強制型別轉換,得到使用者所需要的值
②計算出本引數的實際大小,將指標調到本引數的結尾,也就是下乙個引數的首位址,以便後續處理。
(5)va_
end(va_list)的作用是
把va_list指標
清為null
,x86
平台定義為
ap=(char*)0;
使va_list
不再指向堆疊
,而是跟
null一樣.
有些直接定義為
((void*)0),
這樣編譯器不會為
va_end
產生**,例如
gcc在
linux
的x86
平台就是這樣定義的
. 在這裡大家要注意乙個問題
:由於引數的位址用於
va_start巨集,
所以引數不能宣告為暫存器變數或作為函式或陣列型別
.
函式體內可以多次遍歷這些引數,但是都必須以va—
start
開始,並以va—
end結尾。
va_arg()整型只能用int表示,不能用short否則編譯會有警告,執行會出錯;
浮點數只能用double,不能用float,否則結果同上;
指標型別可以使用;
使用可變引數要注意的問題:
1.標準c庫中的三個巨集作用只是用來確定可變引數列表中每個引數的記憶體位址,編譯器是不知道實際引數的數目的;
①在固定引數中設定標誌——printf函式就是用第乙個固定字串引數確定的;
②在預先設定乙個特殊的結束標記,就是說多輸入乙個可變引數,呼叫時將最後乙個可變引數的值設定成這個特殊的值,在函式體中根據這個值判斷是否達到引數的結尾;
無論什麼方法,程式設計師都應該在文件中告訴呼叫者自己約定;
2.實現可變引數的要點是想辦法取得每個引數的位址,取得位址的辦法由以下幾個因素決定:函式棧的生長方向,引數的入棧順序,cpu對齊方式,記憶體位址表達方式;va_list的實現方式是由記憶體位址的表達方式決定的,_intsizeof(n)的引入規則是由cpu對齊方式決定的,它和第乙個和第二個一起決定了va_start的實現;
3.取得位址後,再結合引數型別,就可正確處理引數;
《the c programming language》
C語言中的不定引數
1,最近剛剛知道c語言還有不定引數這麼個東東。2,解決方法 三個巨集的使用va arg va start 和va end 上述的巨集原型如下所示 type va arg va list argptr,type void va end va list argptr void va start va l...
c語言中處理不定引數
c語言中處理不定引數數目 在程序中,堆疊位址是從高到低分配的.當執行乙個函式的時候,將引數列表入棧,壓入堆疊的高位址部分,然後入棧函式的返回位址,接著入棧函式的執行 這個入棧過程,堆疊位址不斷遞減,一些黑客就是在堆疊中修改函式返回位址,執行自己的 來達到執行自己插入的 段的目的.在函式呼叫時,第乙個...
C語言中不定引數個數的函式
c 中有函式過載這種方法,以供我們呼叫時要可以不確定實參的個數,其實c語言也可以,而且更高明!我們在stdio.h中可以看到printf 函式的原型 int printf char format,事實上,我們如果要寫這樣的函式也可以類似的寫,那麼在定義函式時用上這個符號 它叫佔位符,喊它 三個點 也...