今天在c和指標書中第一次接觸到這個函式,以前有碰到過這樣可變引數的問題,但無從下手。原來c語言還是有這樣的功能,感覺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和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中,所以用到可變引數的程式應該包含這個標頭檔案.下面我們寫乙個簡單的可變引數的函式,改函式至少有乙個整數引數,第二個引數也是整數,是可選的.函式只是列印這兩個引數的值
#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語言函式可變引數
翻apue的時候,看到了一組可變引數的巨集就是va list 類似 void print arg int count,1 va list變數 ifdef m alpha typedef struct va list else typedef char va list 這個 endif 2 intsi...
C語言 可變引數函式
可變引數函式,即引數個數可變的函式。返回值 函式名 固定引數m個,可變引數n個 其中,m 1,n 0,即 至少需要乙個固定引數,否則你怎麼定位到引數呢?固定引數的宣告與普通函式引數相同 可選引數由於數目不定 0個或以上 宣告時用 表示 用作引數佔位符 固定引數和可選引數共同構成可變引數函式的引數列表...
C語言可變引數函式探秘
c語言的可變引數函式看起來是不很酷,必須printf之類的,初學時,一頭霧水,不禁感覺到宇宙的浩瀚和自己的渺小啊,但是等你知道其中原理之後,也就淡定了 下面首先看乙個程式 include stdarg.h include double addmanynum int n,va end arglist ...