C C 要點全掌握(六) 變長引數

2021-06-16 08:05:39 字數 2075 閱讀 2887

上接c/c++要點全掌握(五)——mutable、volatile  

設計乙個引數個數可變、引數型別不定的函式是可能的,最常見的例子是printf函式、scanf函式和高階語言的format函式。在c/c++中,為了通知編譯器函式的引數個數和型別可變(即是不定的、未知的),就必須以三個點結束該函式的宣告。

// printf函式的宣告

int printf(const char * _format, ...);

//scanf函式宣告

int scanf(const char * _format, ...);

//自定義變長引數函式func的宣告

int func(int a,int b,...);

上面func函式的宣告指出該函式至少有兩個整型引數和緊隨其後的0個或多個型別未知的引數。在c/c++中,任何使用變長引數宣告的函式都必須至少有乙個指定的引數(又稱強制引數),即至少有乙個引數的型別是已知的,而不能用三個點省略所有引數的指定,且已知的指定引數必須宣告在函式最左端。

//下面這種宣告是非法的

int func(...);//錯誤

int func(...,int a);//錯誤

含有變長引數的函式是怎麼實現的呢?變長引數函式的實現其實關鍵在於怎麼使用引數,指定了的引數好說,直接使用指定的引數名稱訪問,但未指定的引數呢?我們知道函式呼叫過程中引數傳遞是通過棧來實現的,一般呼叫都是從右至左的順序壓引數入棧,因此引數與引數之間是相鄰的,知道前乙個引數的型別及位址,根據後乙個引數的型別就可以獲取後乙個引數的內容。對於變長引數函式,結合一定的條件,我們可以根據最後乙個指定引數獲取之後的省略引數內容。如,對於函式func,我們知道了引數b的位址及型別,就可知道第乙個可變引數的棧位址(如果有的話),如果知道第乙個可變引數的型別,就可知道第乙個可變引數的內容和第二個可變引數的位址(如果有的話)。以此類推,可以實現對可變引數函式的所有引數的訪問。

那麼,要怎麼指定上訴的「一定的條件」呢?最簡單的方法就像printf等函式一樣,使用格式化佔位符。分析格式化字串引數,通過事先定義好的格式化佔位符可知可變引數的型別及個數,從而獲取各個引數內容。一般對於可變引數型別相同的函式也可直接在強制引數中指定可變引數的個數和型別,這樣也能獲取各個引數的內容。

無論哪種,都涉及對棧位址偏移的操作。結合棧儲存模式和系統資料型別的字長,我們可根據可變引數的型別很容易得到棧位址的偏移量。這裡簡單介紹使用va_start、va_arg、va_end三個標準巨集來實現棧位址的偏移及獲取可變引數內容。這三個巨集定義在stdarg.h標頭檔案中,他們可根據預先定義的系統平台自動獲取相應平台上各個資料型別的偏移量。

//訪問可變引數流程

va_list args; //定義乙個可變引數列表

va_start(args,arg);//初始化args指向強制引數arg的下乙個引數;

va_arg(args,type);//獲取當前引數內容並將args指向下乙個引數

...//迴圈獲取所有可變引數內容

va_end(args);//釋放args

實現乙個簡單的變長引數函式:

//sum為求和函式,其引數型別都為int,但引數個數不定

//第乙個引數(強制引數)n指定後面有多少可變引數

int sum(unsigned int n,...)

va_end(args);

return sum;

}

對於可變引數函式的呼叫有一點需要注意,實際的可變引數的個數必須比前面強制引數中指定的個數要多,或者不小於,也即後續引數多一點不要緊,但不能少,如果少了則會訪問到函式引數以外的堆疊區域,這可能會把程式搞崩掉。前面強制引數中指定的型別和後面實際引數的型別不匹配也有可能造成程式崩潰。

擁有變長引數的函式在宣告定義時其引數個數與型別是不定的,在執行呼叫時引數的狀態則是一定的。而預設引數函式在宣告定義時其引數型別與個數都是一定的,只是後面部分引數指定了預設值,可通過省略(不指定)部分引數呼叫這個預設引數函式。但是預設引數函式還是使用了宣告中指定的全部引數,只不過編譯器做了個順水人情,自動給後部分引數賦了預設值;而變長引數函式則僅僅使用了執行呼叫時提供的引數。

C C 要點全掌握 一 基礎概念

最近參加了幾個公司到學校的招聘會,筆試時大多要求使用c c 語言,而且有些筆試考到了語言的細節部分。說來汗顏,c c 只在大一學習時使用了一年,之後一直在學習高階語言的元件開發,到現在c c 語言的有些細節,部分已經忘記。在此將c c 的基礎要點重拾一遍,以便往後忘記之時查閱。1 區域性變數儲存於堆...

C C 變長引數列表

c 程式設計師經常需要處理c語言下程式 這裡,先展示一下,c 中用c語言的方法寫乙個帶有變長引數的函式,and呼叫。include using std cout using std endl include 等等用到的巨集定義va list等等在這個c的標頭檔案中 帶有變長引數的函式,實現n個dou...

C C 函式變長引數列表實現

在c編譯器通常提供了一系列處理可變引數的巨集,實現就像printf 那樣的變長引數列表,這樣可以遮蔽不同的硬體平台造成的差異,增加程式的可移植性。這些巨集包括va start va arg和va end等,這些巨集都是在標頭檔案裡定義的。採用ansi標準形式時,引數個數可變的函式的原型宣告是 typ...