在初學c的時候,我們都會用到printf函式來寫hello world的程式.在我們看printf函式的宣告時,會看到類似於下面**
int printf(constchar * __restrict, ...);
另外,在我們學習c和c++的時候,函式的宣告總是確定個數和型別的,而我們在用printf的時候,卻可以一次輸出多個引數.
這就是我們要提的不定引數了.
在32位的c和c++程式設計中,函式呼叫是有規約的,並且各個編譯器也基本達成了一致,儘管他們編譯出的東西基本不能通用.關於呼叫規約的東西,可以參考一下維基百科.
在64位的c和c++程式設計中,就沒有呼叫規約的概念了,基本上做到了統一,但是不同的編譯器的傳參方式卻不盡相同,這裡我們不去討論了,有興趣的可以編譯到彙編**檢視.
不定引數的函式呼叫方式為cdec方式的,也就是由呼叫者來恢復引數棧,這個不難理解,因為被呼叫的函式無從得知有多少個引數傳進來,所以不可能知道如何恢復棧.
如果上面寫的你看不懂,不要緊,你可以用google搜一下,相信很快你就會明白了,不搜也不要緊.
在使用不定引數時,我們會用到三個巨集,分別是
va_start , va_arg 和 va_end
還有乙個型別 va_list
它們都定義在 stdarg.h 或者 cstdarg (c++)裡,使用時記得引入.
其中,va_start用來用於初始化va_list, va_arg用來讀取va_list中的引數,當所有的讀取都結束後要用va_end來釋放va_list.
下面寫乙個示例
#include#include#include
int sum(int
count, ...);
intmain()
int sum(int
count, ...)
va_end(arg_ptr);
return
_sum;
}
上面的例子比較簡單,後續引數個數由第乙個引數指定,而且型別預設都是int型別的.
在printf函式中,後續引數個數是由第乙個格式字串來指定的,並且指定了引數型別,比如%d 說明對應的引數是整形而%f 對應的是浮點型別.
接下來我們看看這幾個巨集.
va_start是用來初始化va_list的,第乙個引數是參數列的指標,第二個引數是不定引數前的最後乙個引數.
va_arg 是用來讀取不定引數,第乙個引數是參數列的指標,第二個引數是引數的型別.函式本身並不知道引數的型別,所以使用不當會導致出錯.
va_end是用來釋放va_list占用的資源的,只有乙個引數,就是要釋放的va_list.
最後,我們通過printf函式來總結一下使用不定引數的一些規範:
1. 函式本身必須有辦法知道不定引數的型別,比如printf通過格式化字串通知函式後續引數的每個引數的型別,其中的%d類形的格式與後續的引數是一一對應的.在我寫的示例**中,是預設約定了所有引數都是int 型別的.
2. 函式必須能知道引數結束的地方,printf函式是由格式輸出字串來知道的,當沒有類似%d或%f 這種字元出現時,引數就結束了.我給出的例子中,是通過第乙個引數給定了後續引數的個數的.
3.呼叫必須嚴格按照呼叫的約定來做,而且不定的引數是不會自動轉型的,比如當我們 printf("%d",3.3) 會發現輸出的不是3,就是因為3.3作為乙個浮點數傳入,而不會因為格式字串中的%d自動轉成整數.要想得到預期的結果,需要寫成下面這樣 printf("%d",(int)3.3)
C和C 中的不定引數
在初學c的時候,我們都會用到printf函式來寫hello world的程式.在我們看printf函式的宣告時,會看到類似於下面 int printf const char restrict,另外,在我們學習c和c 的時候,函式的宣告總是確定個數和型別的,而我們在用printf的時候,卻可以一次輸出...
C和C 中的不定引數
在初學c的時候,我們都會用到printf函式來寫hello world的程式.在我們看printf函式的宣告時,會看到類似於下面 int printf const char restrict,另外,在我們學習c和c 的時候,函式的宣告總是確定個數和型別的,而我們在用printf的時候,卻可以一次輸出...
c 不定引數
va list vlist va start vlist,pszformat pszformat 表示 的前乙個引數 va end vlist 其中 typedef char va list define va start crt va start define va arg crt va arg ...