函式引數與函式呼叫(徹底理解值傳遞與引用傳遞)
函式呼叫的作用:
·用實引數向形式引數傳遞資料;
·為獲得資料引數及在函式體中宣告的變數分配儲存空間;
·中斷現行(呼叫)函式,把流程向被呼叫函式的入口處,開始執行被調函式。
當引數表為空時,表示該函式不從呼叫函式接受資料。
函式引數傳遞機制
堆疊儲存區是主調函式(過程)和被呼叫函式(過程)在呼叫發生時進行通訊的主要區域。
基本的引數傳遞機制有兩種:值傳遞和引用傳遞。
值傳遞(passl-by-value): 被調函式的形式引數作為被調函式的區域性變數處理,即在堆疊中開闢了
記憶體空間以存放由主調函式放進來的實參的值,從而成為了實參的一個副本。值傳遞的特點是被調
函式對形式引數的任何操作都是作為區域性變數進行,不會影響主調函式的實參變數的值。
引用傳遞(pass-by-reference): 被調函式的形式引數雖然也作為區域性變數在堆疊中開闢了記憶體空間
,但是這時存放的是由主調函式放進來的實參變數的地址。被調函式對形參的任何操作都被處理成
間接定址,即通過堆疊中存放的地址訪問主調函式中的實參變數。正因為如此,被調函式對形參做
的任何操作都影響了主調函式中的實參變數。
特殊情況:常引數的傳遞
在應用中引數經常使用常指標和常引用,這樣的引數稱為常引數,使用引數不會更新摸個引數指向的物件,在引數傳遞的過程中就不需要執行復制建構函式,從而改善效率。
c++ 中經常使用的是常量引用,如 Swap2(const int& x; const int& y)
這時將不能在函式中修改引用地址所指向的內容,具體來說,x和y將不能出現在"="的左邊。
例項:
修改指標引數的地址,(區分2級指標)
int m=10;
int*n=&m;
void swap(int*&a)
{
a=n; //a地址被n替換
}
/******
值傳遞 的引數有2種--指標變數和非指標變數。當形參為指標變數的時候,要區分於引用傳遞,指
針變數 通過值傳遞過來的是一個地址的值,在被調函式中,只是修改了記憶體地址中存放的值,而並
沒有修改地址值。
在C語言中,值傳遞是唯一可用的引數傳遞機制。
******/
由於受指標變數作為函式引數的影響,被認為是引用傳遞。這是錯誤的。
由上述彙編程式碼基本上說明了C語言中值傳遞的原理,只不過傳遞的是指標的值而已。本文後面還要論述使用引用傳遞的swap函式。從這些彙編程式碼分析,這裡我們可以得到以下幾點:
1.程序的堆疊儲存區是主調函式和被調函式進行通訊的主要區域。
2. C語言中引數是從右向左進棧的。
3. 被調函式使用的堆疊區域結構為:
區域性變數(如temp)
返回地址
函式引數
低地址
高地址
4. 由主調函式在呼叫後清理堆疊。
5.函式的返回值一般是放在暫存器中的。
補充說明幾點:一是引數進棧的方式。對於內部型別,由於編譯器知道各型別變數使用的記憶體大小故直接使用push指令;對於自定義的型別(如structure),採用從源地址向目的(堆疊區)地址進行位元組傳送的方式入棧。二是函式返回值為什麼一般放在暫存器中,這主要是為了支援中斷;如果放在堆疊中有可能因為中斷而被覆蓋。三是函式的返回值如果很大,則從堆疊向存放返回值的地址單元(由主調函式在呼叫前將此地址壓棧提供給被調函式)進行位元組傳送,以達到返回的目的。對於第二和第三點,《Thinking inC++》一書在第10章有比較好的闡述。四是一個顯而易見的結論,如果在被調函式中返回區域性變數的地址是毫無意義的;因為區域性變數存於堆疊中,呼叫結束後堆疊將被清理,這些地址就變得無效了。
C++語言中的函式引數傳遞機制
C++既有C的值傳遞又有引用傳遞。在值傳遞上與C一致,這裡著重說明引用傳遞。如本文前面所述,引用傳遞就是傳遞變數的地址到被調函式使用的堆疊中。在C++中宣告引用傳遞要使用"&"符號,而呼叫時則不用。下面的程式碼是使用引用傳遞的swap2函式和main函式
可以看出,swap2與前面的swap函式的彙編程式碼是一樣的。這是因為前面的swap函式接受指標變數,而指標變數的值正是地址。所以,對於這裡的swap2和前面的swap來講,堆疊中的函式引數存放的都是地址,在函式中操作的方式是一致的。但是,對swap2來說這個地址是主調函式通過將實參變數的偏移地址壓棧而傳遞進來的--這是引用傳遞;而對swap來說,這個地址是主調函式通過將實參變數的值壓棧而傳遞進來的--這是值傳遞,只不過由於這個實參變數是指標變數所以其值是地址而已。
這裡的關鍵點在於,同樣是地址,一個是引用傳遞中的變數地址,一個是值傳遞中的指標變數的值。我想若能明確這一點,就不至於將C語言中的以指標變數作為函式引數的值傳遞情況混淆為引用傳遞了。
雖然x是一個區域性變數,但是由於其值是主調函式中的實參變數的地址,故在swap2中返回這個地址是合法的。
四、 結束語
本文論述了在 C 和 c++ 中函式呼叫的引數傳遞機制;同時附帶說明了函式返回值的一些問題。本文示例使用的是VC++6.0。
可見值傳遞是傳輸了要傳遞的變數的一個副本,所以改變這個副本不會對呼叫函式造成影響,但是這個被呼叫函式一般有一個有用的返回值,也就是你用某個東西,在使用過程中,也許改變了它,但是時候後,你又保持原樣給了人家。比如給你一個打好節的絲巾,你使用時換了另一種樣式,照了像,還別人的時候,又按照人家的借你的樣子還給人家,而這個照片就是需要得到的東西(類似返回值)。
而引用,就是將要傳遞的變數的地址傳到了被呼叫函式中,如果在被呼叫函式中改變,那麼就會在呼叫函式中改變。比如你借了人家布,如果你剪裁了不同的樣式,那麼還人家的樣子就是你剪裁後的樣子。一般c++可以使用值傳遞和引用傳遞,後者更多。因為這樣不用另外在堆疊中開闢空間,而值傳遞就需要另外的開闢空間,對記憶體有一定的浪費。一般c中只使用值傳遞。
另外關於儲存資料方面,一般是將區域性變數,函式返回地址,函式引數放到堆疊中,而函式返回值一般放到暫存器中,為的是方便中斷,如果有零時中斷就可以直接從暫存器中處理,不用再進行壓棧出棧操作。
//錯誤的 得到的是亂碼
char *strchr(char *str,char character)
{
char* substr;
substr[0]=str[0];
return substr;
}