C/C++中的函數參數傳遞機制

網站建設、網站設計,成都做網站公司-創(chuàng)新互聯已向上千家企業(yè)提供了,網站設計,網站制作,網絡營銷等服務!設計與技術結合,多年網站推廣經驗,合理的價格為您打造企業(yè)品質網站。
一、 函數參數傳遞機制的基本理論
函數參數傳遞機制問題在本質上是調用函數(過程)和被調用函數(過程)在調用發(fā)生時進行通信的方法問題。基本的參數傳遞機制有兩種:值傳遞和引用傳遞。以下討論稱調用其他函數的函數為主調函數,被調用的函數為被調函數。
值傳遞(passl-by-value)過程中,被調函數的形式參數作為被調函數的局部變量處理,即在堆棧中開辟了內存空間以存放由主調函數放進來的實參的值,從而成為了實參的一個副本。值傳遞的特點是被調函數對形式參數的任何操作都是作為局部變量進行,不會影響主調函數的實參變量的值。
引用傳遞(pass-by-reference)過程中,被調函數的形式參數雖然也作為局部變量在堆棧中開辟了內存空間,但是這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操作都被處理成間接尋址,即通過堆棧中存放的地址訪問主調函數中的實參變量。正因為如此,被調函數對形參做的任何操作都影響了主調函數中的
實參變量。
二、 C語言中的函數參數傳遞機制
在C語言中,值傳遞是唯一可用的參數傳遞機制。但是據筆者所知,由于受指針變量作為函數參數的影響,有許多朋友還認為這種情況是引用傳遞。這是錯誤的。請看下面的代碼:
int swap(int *x, int *y)
{
int temp;
temp = *x; *x = *y; *y = temp;
return temp;
}
void main()
{
int a = 1, b = 2;
int *p1 = a;
int *p2 = b;
swap(p1, p2)
}
函數swap以兩個指針變量作為參數,當main()調用swap時,是以值傳遞的方式將指針變量p1、p2的值(也就是變量a、b的地址)放在了swap在堆棧中為形式參數x、y開辟的內存單元中。這一點從以下的匯編代碼可以看出(注釋是筆者加的):
22: void main()
23: {
……
……
13: int a = 1, b = 2;
00401088 mov dword ptr [ebp-4],1
0040108F mov dword ptr [ebp-8],2
14: int *p1 = a;
00401096 lea eax,[ebp-4]
00401099 mov dword ptr [ebp-0Ch],eax
15: int *p2 = b;
0040109C lea ecx,[ebp-8]
0040109F mov dword ptr [ebp-10h],ecx
16: swap(p1, p2);
004010A2 mov edx,dword ptr [ebp-10h] ;參數p2的值進棧
004010A5 push edx
004010A6 mov eax,dword ptr [ebp-0Ch] ;參數p1的值進棧
004010A9 push eax
004010AA call @ILT+15(swap) (00401014) ;調用swap函數
004010AF add esp,8 ;清理堆棧中的參數
17: }
閱讀上述代碼要注意,INTEL80x86系列的CPU對堆棧的處理是向下生成,即從高地址單元向低地址單元生成。從上面的匯編代碼可知,main() 在調用swap之前,先將實參的值按從右至左的順序壓棧,即先p2進棧,再p1進棧。調用結束之后,主調函數main()負責清理堆棧中的參數。Swap 將使用這些進入堆棧的變量值。下面是swap函
數的匯編代碼:
14: void swap(int *x, int *y)
15: {
00401030 push ebp
00401031 mov ebp,esp ;ebp指向棧頂
……
……
16: int temp;
17: temp = *x;
4: int temp;
5: temp = *x;
00401048 mov eax,dword ptr [ebp+8] ;操作已存放在堆棧中的p1,將p1置
; 入eax
0040104B mov ecx,dword ptr [eax] ;通過寄存器間址將*p1置入ecx
0040104D mov dword ptr [ebp-4],ecx;經由ecx將*p1置入temp變量的內
;存單元。以下類似
6: *x = *y;
00401050 mov edx,dword ptr [ebp+8]
00401053 mov eax,dword ptr [ebp+0Ch]
00401056 mov ecx,dword ptr [eax]
00401058 mov dword ptr [edx],ecx
7: *y = temp;
0040105A mov edx,dword ptr [ebp+0Ch]
0040105D mov eax,dword ptr [ebp-4]
00401060 mov dword ptr [edx],eax
8: return temp;
00401062 mov eax,dword ptr [ebp-4]
9: }
由上述匯編代碼基本上說明了C語言中值傳遞的原理,只不過傳遞的是指針的值而已。本文后面還要論述使用引用傳遞的swap函數。從這些匯編代碼分析,這里我們可以得到以下幾點:
1. 進程的堆棧存儲區(qū)是主調函數和被調函數進行通信的主要區(qū)域。
2. C語言中參數是從右向左進棧的。
3. 被調函數使用的堆棧區(qū)域結構為:
局部變量(如temp)
返回地址
函數參數
低地址
高地址
4. 由主調函數在調用后清理堆棧。
5. 函數的返回值一般是放在寄存器中的。
這里尚需補充說明幾點:一是參數進棧的方式。對于內部類型,由于編譯器知道各類型變量使用的內存大小故直接使用push指令;對于自定義的類型(如 structure),采用從源地址向目的(堆棧區(qū))地址進行字節(jié)傳送的方式入棧。二是函數返回值為什么一般放在寄存器中,這主要是為了支持中斷;如果放在堆棧中有可能因為中斷而被覆蓋。三是函數的返回值如果很大,則從堆棧向存放返回值的地址單元(由主調函數在調用前將此地址壓棧提供給被調函數)進行字節(jié)傳送,以達到返回的目的。對于第二和第三點,《Thinking in C++》一書在第10章有比較好的闡述。四是一個顯而易見的結論,如果在被調函數中返回局部變量的地址是毫無意義的;因為局部變量存于堆棧中,調用結束后堆棧將被清理,這些地址就變得無效了。
三、 C++語言中的函數參數傳遞機制
C++既有C的值傳遞又有引用傳遞。在值傳遞上與C一致,這里著重說明引用傳遞。如本文前面所述,引用傳遞就是傳遞變量的地址到被調函數使用的堆棧中。在C++中聲明引用傳遞要使用""符號,而調用時則不用。下面的代碼是使用引用傳遞的swap2函數和main函數:
int swap2(int x, int y)
{
int temp;
temp = x;
x = y;
y = temp;
return x;
}
void main()
{
int a = 1, b = 2;
swap2(a, b);
}
此時函數swap2將接受兩個整型變量的地址,同時返回一個其中的一個。而從main函數中對swap2的調用swap2(a, b)則看不出是否使用引用傳遞,是否使用引用傳遞,是由swap2函數的定義決定的。以下是main函數的匯編代碼:
11: void main()
12: {
……
……
13: int a = 1, b = 2;
00401088 mov dword ptr [ebp-4],1 ;變量a
0040108F mov dword ptr [ebp-8],2 ;變量b
14: swap2(a, b);
00401096 lea eax,[ebp-8] ;將b的偏移地址送入eax
00401099 push eax ;b的偏移地址壓棧
0040109A lea ecx,[ebp-4] ;將a的偏移地址送入ecx
0040109D push ecx ;將a的偏移地址壓棧
0040109E call @ILT+20(swap2) (00401019) ;調用swap函數
004010A3 add esp,8 ;清理堆棧中的參數
15: }
可以看出,main函數在調用swap2之前,按照從右至左的順序將b和a的偏移地
址壓棧,這就是在傳遞變量的地址。此時swap2函數的匯編代碼是:
2: int swap2(int x, int y)
3: {
00401030 push ebp
00401031 mov ebp,esp
……
……
4: int temp;
5: temp = x;
00401048 mov eax,dword ptr [ebp+8]
0040104B mov ecx,dword ptr [eax]
0040104D mov dword ptr [ebp-4],ecx
6: x = y;
00401050 mov edx,dword ptr [ebp+8]
00401053 mov eax,dword ptr [ebp+0Ch]
00401056 mov ecx,dword ptr [eax]
00401058 mov dword ptr [edx],ecx
7: y = temp;
0040105A mov edx,dword ptr [ebp+0Ch]
0040105D mov eax,dword ptr [ebp-4]
00401060 mov dword ptr [edx],eax
8: return x;
00401062 mov eax,dword ptr [ebp+8] ;返回x,由于x是外部變量的偏移地
;址,故返回是合法的
9: }
可以看出,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。
先學指針再學函數為好。函數調用,若想通過參數帶回函數運算結果,要用指針。函數調用,若想通過參數傳入可變大小的數組,要用指針。 這時若缺乏 指針的知識,則難以學好函數的書寫。當然,你可以先學簡單的函數,參數不用指針的,只通過函數返回或全局量傳遞運算結果,只用固定大小的數組。學會后,再學指針和 用指針做參數的函數。這樣,路走得慢些,但學得可能扎實些。
01
指針函數通常是指函數返回值是指針的一類函數,如圖所示。
02
函數指針是指指向某個具體函數的指針變量,在程序設計時可以用來調用某個特定函數或者做某個函數的參數。其形式一般如圖:
03
指針函數與函數指針本質上的區(qū)別是,指針函數是一個帶指針的函數,總的來說還是一個函數,如圖就是一個帶*name指針的函數
04
函數指針是指向函數的指針變量,本質上還是一個指針,其格式如下,可以看到和指針函數的格式非常像,所以一定要用心留意。
本質的區(qū)別是:
一個是函數 一個是指針變量
1、指針函數是指帶指針的函數,即本質是一個函數。函數返回類型是某一類型的指針
類型標識符 *函數名(參數表)
int *f(x,y);
2、函數指針是指向函數的指針變量,即本質是一個指針變量。
int (*f) (int x); /* 聲明一個函數指針 */
f=func; /* 將func函數的首地址賦給指針f */
希望對您有所幫助
這么給你解釋吧,指針就相當是一個門牌號。第一個呢,是因為f()函數的參數是指針,就是“門牌號”,所以他們交換的時候是把門牌號進行了交換。所以當你第二次進行訪問的時候,原來A房間變成了B房間,所以當你想要讓A房間里的人出來的時候,這個時候原來住在里面的B君就出來了。所以發(fā)生了交換。
第二個呢,傳的是形參,形參就是把A君復制一個(我們理解為克隆一個A君),然后克隆一個B君,最后把克隆人交換。然后我們輸出的時候,我們卻要A房間里的人,那個時候,A君還在自己的房間,當然他們出來的時候,就是沒有交換了。如果,要交換,我們可以輸出克隆人。那么看到的結果就是已經發(fā)生了交換。
我把第二個代碼,給你稍微改下,就是輸出克隆人,那么你就會看到,我們原來交換的只是克隆人了。
============
第二個
#includestdio.h
void main()
{void f(int x,int y);
int a,b;
scanf("%d,%d",a,b);
f(a,b);
}
void f(int x,int y)
{int p;
p=x;
x=y;
y=p;
printf("%d,%d",x,y);
}
輸入5,9
輸出9,5
===========
有不明白的繼續(xù)追問!
文章標題:c語言中函數與指針的探討 c語言中指針函數的用法
轉載源于:http://chinadenli.net/article48/hppohp.html
成都網站建設公司_創(chuàng)新互聯,為您提供微信公眾號、動態(tài)網站、網站設計、移動網站建設、營銷型網站建設、外貿建站
聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯