一、 函式
引數傳遞機制的基本理論
函式引數傳遞機制問題在本質上是呼叫函式(過程)和被呼叫函式(過程)在呼叫發生時進行通訊
的方法問題。基本的引數傳遞機制有兩種:值傳遞和引用傳遞。以下討論稱呼叫其他函式的函式為主調函式,被呼叫的函式為被調函式。
值傳遞(passl-by-value)過程中,被調函式的形式引數作為被調函式的區域性變數處理,即在堆疊中開闢了
記憶體空間以存放由主調函式放進來的實參的值,從而成為了實參的乙個副本。值傳遞的特點是被調函式對形式引數的任何操作都是作為區域性變數進行,不會影響主調函式的實參變數的值。
引用傳遞(pass-by-reference)過程中,被調函式的形式引數雖然也作為區域性變數在堆疊中開闢了記憶體空間,但是這時存放的是由主調函式放進來的實參變數的位址。被調函式對形參的任何操作都被處理成間接定址,即通過堆疊中存放的位址訪問主調函式中的實參變數。正因為如此,被調函式對形參做的任何操作都影響了主調函式中的實參變數。
二、 c語言中的函式引數傳遞機制
在c語言中,值傳遞是唯一可用的引數傳遞機制。但是據筆者所知,由於受指標變數作為函式引數的影響,有許多朋友還認為這種情況是引用傳遞。這是錯誤的。請看下面的**:
int swap(int *x, int *y)
void main()
函式swap以兩個指標變數作為引數,當main()呼叫swap時,是以值傳遞的方式將指標變數p1、p2的值(也就是變數a、b的位址)放在了swap在堆疊中為形式引數x、y開闢的記憶體單元中。這一點從以下的
彙編**可以看出(注釋是筆者加的):
22: void main()
23:
閱讀上述**要注意,intel80x86系列的cpu對堆疊的處理是向下生成,即從高位址單元向低位址單元生成。從上面的彙編**可知,main()在呼叫swap之前,先將實參的值按從右至左的順序壓棧,即先p2進棧,再p1進棧。呼叫結束之後,主調函式main()負責清理堆疊中的引數。swap 將使用這些進入堆疊的變數值。下面是swap函式的彙編**:
14: void swap(int *x, int *y)
15:
由上述彙編**基本上說明了c語言中值傳遞的原理,只不過傳遞的是指標的值而已。本文後面還要論述使用引用傳遞的swap函式。從這些彙編**分析,這裡我們可以得到以下幾點:
1.程序的堆疊儲存區是主調函式和被調函式進行通訊的主要區域。
2. c語言中引數是從右向左進棧的。
3. 被調函式使用的堆疊區域結構為:
區域性變數(如temp)
返回位址
函式引數
低位址高位址
4. 由主調函式在呼叫後清理堆疊。
5.函式的返回值一般是放在暫存器中的。
這裡尚需補充說明幾點:一是引數進棧的方式。對於內部型別,由於編譯器知道各型別變數使用的記憶體大小故直接使用push指令;對於自定義的型別(如structure),採用從源位址向目的(堆疊區)位址進行位元組傳送的方式入棧。二是函式返回值為什麼一般放在暫存器中,這主要是為了支援中斷;如果放在堆疊中有可能因為中斷而被覆蓋。三是函式的返回值如果很大,則從堆疊向存放返回值的位址單元(由主調函式在呼叫前將此位址壓棧提供給被調函式)進行位元組傳送,以達到返回的目的。對於第二和第三點,《thinking in
c++》一書在第10章有比較好的闡述。四是乙個顯而易見的結論,如果在被調函式中返回區域性變數的位址是毫無意義的;因為區域性變數存於堆疊中,呼叫結束後堆疊將被清理,這些位址就變得無效了。
三、 c++語言中的函式引數傳遞機制
c++既有c的值傳遞又有引用傳遞。在值傳遞上與c一致,這裡著重說明引用傳遞。如本文前面所述,引用傳遞就是傳遞變數的位址到被調函式使用的堆疊中。在c++中宣告引用傳遞要使用"&"符號,而呼叫時則不用。下面的**是使用引用傳遞的swap2函式和main函式:
int& swap2(int& x, int& y)
void main()
此時函式swap2將接受兩個整型變數的位址,同時返回乙個其中的乙個。而從main函式中對swap2的呼叫swap2(a, b)則看不出是否使用引用傳遞,是否使用引用傳遞,是由swap2函式的定義決定的。以下是main函式的彙編**:
11: void main()
12:
可以看出,main函式在呼叫swap2之前,按照從右至左的順序將b和a的偏移地
址壓棧,這就是在傳遞變數的位址。此時swap2函式的
彙編**是:
2: int& swap2(int& x, int& y)
3: 可以看出,swap2與前面的swap函式的彙編**是一樣的。這是因為前面的swap函式接受指標變數,而指標變數的值正是位址。所以,對於這裡的swap2和前面的swap來講,堆疊中的函式引數存放的都是位址,在函式中操作的方式是一致的。但是,對swap2來說這個位址是主調函式通過將實參變數的偏移位址壓棧而傳遞進來的--這是引用傳遞;而對swap來說,這個位址是主調函式通過將實參變數的值壓棧而傳遞進來的--這是值傳遞,只不過由於這個實參變數是指標變數所以其值是位址而已。
這裡的關鍵點在於,同樣是位址,乙個是引用傳遞中的變數位址,乙個是值傳遞中的指標變數的值。我想若能明確這一點,就不至於將c語言中的以指標變數作為函式引數的值傳遞情況混淆為引用傳遞了。
雖然x是乙個區域性變數,但是由於其值是主調函式中的實參變數的位址,故在swap2中返回這個位址是合法的。
c++ 中經常使用的是常量引用,如將swap2改為:
swap2(const int& x; const int& y)
這時將不能在函式中修改引用位址所指向的內容,具體來說,x和y將不能出現在"="的左邊。
四、 結束語
本文論述了在 c 和 c++ 中函式呼叫的引數傳遞機制;同時附帶說明了函式返回值的一些問題。本文示例使用的是vc++6.0。
可見值傳遞是傳輸了要傳遞的變數的乙個副本,所以改變這個副本不會對呼叫函式造成影響,但是這個被呼叫函式一般有乙個有用的返回值,也就是你用某個東西,在使用過程中,也許改變了它,但是時候後,你又保持原樣給了人家。比如給你乙個打好節的絲巾,你使用時換了另一種樣式,照了像,還別人的時候,又按照人家的借你的樣子還給人家,而這個**就是需要得到的東西(類似返回值)。
而引用,就是將要傳遞的變數的位址傳到了被呼叫函式中,如果在被呼叫函式中改變,那麼就會在呼叫函式中改變。比如你借了人家布,如果你剪裁了不同的樣式,那麼還人家的樣子就是你剪裁後的樣子。一般c++可以使用值傳遞和引用傳遞,後者更多。因為這樣不用另外在堆疊中開闢空間,而值傳遞就需要另外的開闢空間,對記憶體有一定的浪費。一般c中只使用值傳遞。
另外關於儲存資料方面,一般是將區域性變數,函式返回位址,函式引數放到堆疊中,而函式返回值一般放到暫存器中,為的是方便中斷,如果有零時中斷就可以直接從暫存器中處理,不用再進行壓棧出棧操作。
C C 中的函式引數傳遞機制
一 函式引數傳遞機制的基本理論 函式引數傳遞機制問題在本質上是呼叫函式 過程 和被呼叫函式 過程 在呼叫發生時進行通訊的方法問題。基本的引數傳遞機制有兩種 值傳遞和引用傳遞。以下討論稱呼叫其他函式的函式為主調函式,被呼叫的函式為被調函式。值傳遞 passl by value 過程中,被調函式的形式引...
C C 中的函式引數傳遞機制
一 函式引數傳遞機制的基本理論 函式引數傳遞機制問題在本質上是呼叫函式 過程 和被呼叫函式 過程 在呼叫發生時進行通訊的方法問題。基本的引數傳遞機制有兩種 值傳遞和引用傳遞。以下討論稱呼叫其他函式的函式為主調函式,被呼叫的函式為被調函式。值傳遞 passl by value 過程中,被調函式的形式引...
C C 中的函式引數傳遞機制
瀏覽選項 大中小 顏色 預設灰度 橄欖色綠色 藍色褐色 紅色文章摘要 本文論述了在 c 和 c 中函式呼叫的引數傳遞機制 同時附帶說明了函式返回值的一些問題。本文示例使用的是vc 6.0。c c 中的函式引數傳遞機制 一 函式引數傳遞機制的基本理論 函式引數傳遞機制問題在本質上是呼叫函式 過程 和被...