C可變引數的實現

2021-08-06 03:53:03 字數 3124 閱讀 6818

原文 >

c函式要在程式中用到以下這些巨集:

void va_start( va_list arg_ptr, prev_param );

type va_arg( va_list arg_ptr, type );

void va_end( va_list arg_ptr );

va_list:用來儲存巨集va_start、va_arg和va_end所需資訊的一種型別。為了訪問變長引數列表中的引數,必須宣告

va_list型別的乙個物件       定義: typedef char *  va_list;

va_start:訪問變長引數列表中的引數之前使用的巨集,它初始化用va_list宣告的物件,初始化結果供巨集va_arg和

va_end使用;

va_arg: 展開成乙個表示式的巨集,該表示式具有變長引數列表中下乙個引數的值和型別。每次呼叫va_arg都會修改

用va_list宣告的物件,從而使該物件指向引數列表中的下乙個引數;

va_end:該巨集使程式能夠從變長引數列表用巨集va_start引用的函式中正常返回。

va在這裡是variable-argument(可變引數)的意思.

這些巨集定義在stdarg.h中,所以用到可變引數的程式應該包含這個標頭檔案.下面我們寫乙個簡單的可變引數的函式,改函式至少有乙個整數引數,第二個引數也是整數,是可選的.函式只是列印這兩個引數的值.

[cpp]view plain

copy

print?

#include ;  

#include ;  

#include ;  

/* ansi標準形式的宣告方式,括號內的省略號表示可選引數 */

int demo(char *msg, … )    

va_end( argp );                                   /* 將argp置為null */

return 0;    

}  void main( void )    

從這個函式的實現可以看到,我們使用可變引數應該有以下步驟: 

1)首先在函式裡定義乙個va_list型的變數,這裡是arg_ptr,這個變 

量是指向引數的指標. 

2)然後用va_start巨集初始化變數arg_ptr,這個巨集的第二個引數是第 

乙個可變引數的前乙個引數,是乙個固定的引數. 

3)然後用va_arg返回可變的引數,並賦值給整數j. va_arg的第二個 

引數是你要返回的引數的型別,這裡是int型. 

4)最後用va_end巨集結束可變引數的獲取.然後你就可以在函式裡使 

用第二個引數了.如果函式有多個可變引數的,依次呼叫va_arg獲 

取各個引數. 

二、可變參型別陷阱

下面的**是錯誤的,執行時得不到預期的結果:

view plaincopy to clipboardprint?

va_start(parg, plotno);  

fvalue = va_arg(parg, float);  // 型別應改為double,不支援float  

va_end(parg); 

va_start(parg, plotno);

fvalue = va_arg(parg, float);  // 型別應改為double,不支援float

va_end(parg);

下面列出va_arg(argp, type)巨集中不支援的type:

—— char、signed char、unsigned char

—— short、unsigned short

—— signed short、short int、signed short int、unsigned short int

—— float

在c語言中,呼叫乙個不帶原型宣告的函式時,呼叫者會對每個引數執行「預設實際引數提公升(default argument promotions)」。該規則同樣適用於可變引數函式——對可變長引數列表超出最後乙個有型別宣告的形式引數之後的每乙個實際引數,也將執行上述提公升工作。

提公升工作如下:

——float型別的實際引數將提公升到double

——char、short和相應的signed、unsigned型別的實際引數提公升到int

——如果int不能儲存原值,則提公升到unsigned int

然後,呼叫者將提公升後的引數傳遞給被呼叫者。

所以,可變參函式內是絕對無法接收到上述型別的實際引數的。

關於該陷井,c/c++著作中有以下描述:

在《c語言程式設計》對可變長引數列表的相關章節中,並沒有提到這個陷阱。但是有提到預設實際引數提公升的規則:

在沒有函式原型的情況下,char與short型別都將被轉換為int型別,float型別將被轉換為double型別。

——《c語言程式設計》第2版  2.7 型別轉換 p36

在其他一些書籍中,也有提到這個規則:

事情很清楚,如果乙個引數沒有宣告,編譯器就沒有資訊去對它執行標準的型別檢查和轉換。

在這種情況下,乙個char或short將作為int傳遞,float將作為double傳遞。

這些做未必是程式設計師所期望的。

腳注:這些都是由c語言繼承來的標準提公升。

對於由省略號表示的引數,其實際引數在傳遞之前總執行這些提公升(如果它們屬於需要提公升的型別),將提公升後的值傳遞給有關的函式。——譯者注

——《c++程式語言》第3版-特別版 7.6 p138

…… float型別的引數會自動轉換為double型別,short或char型別的引數會自動轉換為int型別 ……

——《c陷阱與缺陷》 4.4 形參、實參與返回值 p73

這裡有乙個陷阱需要避免:

va_arg巨集的第2個引數不能被指定為char、short或者float型別。

因為char和short型別的引數會被轉換為int型別,而float型別的引數會被轉換為double型別 ……

例如,這樣寫肯定是不對的:

c = va_arg(ap,char);

因為我們無法傳遞乙個char型別引數,如果傳遞了,它將會被自動轉化為int型別。上面的式子應該寫成:

c = va_arg(ap,int);

——《c陷阱與缺陷》p164

C可變引數函式 實現

c函式要在程式中用到以下這些巨集 void va start va list arg ptr,prev param type va arg va list arg ptr,type void va end va list arg ptr va list 用來儲存巨集va start va arg和v...

C可變引數函式 實現

c函式要在程式中用到以下這些巨集 void va start va list arg ptr,prev param type va arg va list arg ptr,type void va end va list arg ptr va list 用來儲存巨集va start va arg和v...

C可變引數函式 實現

c函式要在程式中用到以下這些巨集 void va start va list arg ptr,prev param type va arg va list arg ptr,type void va end va list arg ptr va list 用來儲存巨集va start va arg和v...