c支援可變引數的函式,這裡的意思是c支援函式帶有可變數量的引數,最常見的例子就
是我們十分熟悉的printf()系列函式。我們還知道在函式呼叫時引數是自右向左壓棧的
。如果可變引數函式的一般形式是:
f(p1, p2, p3, …)
那麼引數進棧(以及出棧)的順序是:
…push p3
push p2
push p1
call f
pop p1
pop p2
pop p3
…我可以得到這樣乙個結論:如果支援可變引數的函式,那麼引數進棧的順序幾乎必然是
自右向左的。並且,引數出棧也不能由函式自己完成,而應該由呼叫者完成。
這個結論的後半部分是不難理解的,因為函式自身不知道呼叫者傳入了多少引數,但是
呼叫者知道,所以呼叫者應該負責將所有引數出棧。
在可變引數函式的一般形式中,左邊是已經確定的引數,右邊省略號代表未知引數部分
。對於已經確定的引數,它在棧上的位置也必須是確定的。否則意味著已經確定的引數
是不能定位和找到的,這樣是無法保證函式正確執行的。衡量引數在棧上的位置,就是
離開確切的函式呼叫點(call f)有多遠。已經確定的引數,它在棧上的位置,不應該
依賴引數的具體數量,因為引數的數量是未知的!
所以,選擇只能是,已經確定的引數,離開函式呼叫點有確定的距離(較近)。滿足這
個條件,只有引數入棧遵從自右向左規則。也就是說,左邊確定的引數後入棧,離函式
呼叫點有確定的距離(最左邊的引數最後入棧,離函式呼叫點最近)。
這樣,當函式開始執行後,它能找到所有已經確定的引數。根據函式自己的邏輯,它負
責尋找和解釋後面可變的引數(在離開呼叫點較遠的地方),通常這依賴於已經確定的
引數的值(典型的如prinf()函式的格式解釋,遺憾的是這樣的方式具有脆弱性)。
據說在pascal中引數是自左向右壓棧的,與c的相反。對於pascal這種只支援固定引數函
數的語言,它沒有可變引數帶來的問題。因此,它選擇哪種引數進棧方式都是可以的。
甚至,其引數出棧是由函式自己完成的,而不是呼叫者,因為函式的引數的型別和數量
是完全已知的。這種方式比採用c的方式的效率更好,因為占用更少的**量(在c中,
函式每次呼叫的地方,都生成了引數出棧**)。
c++為了相容c,所以仍然支援函式帶有可變的引數。但是在c++中更好的選擇常常是函式
過載。
可變引數與引數進棧順序
c支援可變引數的函式,這裡的意思是 c支援函式帶有可變數量的引數,最常見的例子就是我們十分熟悉的 printf 系列函式。我們還知道在函式呼叫時引數是自右向左壓棧的。如果可變引數函式的一般形式是 f p1,p2,p3,那麼引數進棧 以及出棧 的順序是 push p3 push p2 push p1 ...
C 特性探尋 引數傳遞和返回值
對於原始型別 或稱基本型別 如int,char,float,指標 等,引數傳遞和返回值不 會碰到什麼難以理解的問題。能引起關注的焦點是,當我們把物件作為引數傳遞,或者 返回乙個物件時,這裡面發生了什麼?我個人覺得,返回乙個物件 的說法或概念尤其難以理解。例如函式f 返回乙個物件 類a的例項 a a ...
《c和指標》 巨集可變引數
va list 是在c語言中解決變參問題的一組巨集,定義在標頭檔案下。va list的用法 1 首先在函式裡定義一具va list型的變數,這個變數是指向引數的指標 2 然後用va start巨集初始化變數剛定義的va list變數,這個巨集的第二個引數是第乙個可變引數的前乙個引數,是乙個固定的引數...