轉錄至http://wp.mlab.tw/?p=176
何為傳值call by value、傳址call by address、傳參考call by reference?
“也可以叫做pass by value、pass by address、pass by reference”
傳址call by address聽說是台灣人發明的講法,
其實傳址它本質上也是call by value,或者是call by value of pointer,至於為什麼晚點再說明
傳值call by value:
void swap (int c , int d)
{
int temp=c;
c=d;
d=temp;
}
int main()
{
int a=5,b=10;
swap(a,b);
printf(" %d %d ", a,b);
}
|
swap意思是交換,所以這段程式碼主要是想交換主程式a跟b的值,
如果上面的程式碼不太清楚,建議先去網路或看書學一些基本的概念。
在這段程式碼中,我們在主程式宣告了a跟b的值,見下圖:
main的a | main的b | ||||
儲存值 | 5 | 10 | …… | …… | …… |
記憶體位址 | 0x04 | 0x08 | …… | …… | …… |
PS:記憶體的位址應該會是像0x28ff44這種樣子,可是為了方便所以我把它縮短成如上所示
當程式執行第一行的宣告後,系統會立即分配記憶體空間給變數使用,且將儲存的值存到變數裡
接著當下一行我們呼叫了副程式swap(a,b),意思就是要把a跟b的引數給swap副程式c跟d使用
且是按照順序的給下去,swap第一個參數的c對應主程式第一個給的引數a
main的a | main的b | swap的c | swap的d | ||
儲存值 | 5 | 10 | …… | 5 | 10 |
記憶體位址 | 0x04 | 0x08 | …… | 0x16 | 0x20 |
當副程式的程式執行完之後記憶體會變如下:
main的a | main的b | swap的c | swap的d | ||
儲存值 | 5 | 10 | …… | 10 | 5 |
記憶體位址 | 0x04 | 0x08 | …… | 0x16 | 0x20 |
最後回到主程式並印出a跟b的值,會發現a跟b根本沒有改變,為什麼呢?
這就是因為你使用的方式是傳值call by value,傳值的意思顧名思義就是只把”值”複製給對方
對方拿到了你的值以後做任何改變都不會影響到原本的值,因此如果你想要利用副程式把a跟b交換,可以使用傳址
傳址call by address:
void swap (int *c , int *d){
int temp=*c;
*c=*d;
*d=temp;
}
int main(){
int a=5,b=10;
swap(&a,&b);
printf(" %d %d ", a,b);
}
|
在這段程式碼中,我們在主程式宣告了a跟b的值,同樣見下圖:
main的a | main的b | ||||
儲存值 | 5 | 10 | …… | …… | …… |
記憶體位址 | 0x04 | 0x08 | …… | …… | …… |
接著當下一行我們呼叫了副程式swap(&a,&b),意思就是要把a跟b的位址給swap副程式c跟d使用
&的意思就是要領出該變數的”位址”,因此&a的話是把0x04的記憶體位址給對方
而副程式的swap(int *c , int *d)
其中*的意思是”指標Pointer”指標是用來儲存記憶體位址的,
因此這段程式的意思就是”指標c指向a”,執行到副程式後記憶體如下表示:
main的a | main的b | swap的*c | swap的*d | ||
儲存值 | 5 | 10 | …… | 0x04 | 0x08 |
記憶體位址 | 0x04 | 0x08 | …… | 0x16 | 0x20 |
如果你在副程式裡直接執行printf(“%d”,c); 印出來的內容只會是主程式a的記憶體位置,也就是0x04
所以當你想用c來印出a的內容,得用*c才可以印出printf(“%d”,*c);
在這邊*已經不是指標的意思,而是”提領Dereference”,簡單說就是提取本身所指向的位址的值,也就是a的值5
所以副程式執行完交換後記憶體如下表示:
main的a | main的b | swap的*c | swap的*d | ||
儲存值 | 10 | 5 | …… | 0x04 | 0x08 |
記憶體位址 | 0x04 | 0x08 | …… | 0x16 | 0x20 |
所以副程式執行完後,主程式的printf函式就可以順利的印出交換後的a與b。
看到這裡很多人應該都會認為這樣的確是call by address不是嗎?畢竟傳過去的是位址,而不是值
這邊有一段程式碼給大家看看:
int main(){
int a=5,b=10;
int *c=&a;
int *d=c;
printf("*c=%d *d=%d\n",*c,*d);
d=&b;
printf("*c=%d *d=%d\n",*c,*d);
return 0;
}
|
當第一個printf執行後,印出來的c跟d都是5
可是下一行的d=&b執行後在印一次,c卻仍然是5,d卻變成了10
因此你可以這樣想:傳指標,仍然也是call by value,只是那個value是指標本身,
複製的內容也是指標本身,只不過那個值Value剛好就是位址address
所以各位要大概瞭解call by address本質上也是call by value或者叫call by value of pointer
不過畢竟這call by address的講法已經在台灣紮根了,所以個人認為也不用強硬的堅持C語言只有call by value
總結:
指標是專門用來儲存記憶體的位址,但指標本身也有自己的記憶體位址
int *ptr; 這裡的*號代表指標Pointer,在未指向任何位址的指標,你不可以去提領他,這是非法行為
int *ptr2=NULL 如果指標宣告後一開始還沒打算讓他指向任何東西,良好習慣就是給他一個NULL值
int *ptr3 = &x; &x意思是要把x的記憶體位址傳出來給指標ptr3
printf( ” %d ” , *ptr3); 這裡的*號代表提領Dereference,意思是要把本身指向的位址的值提領出來
傳參考call by reference:
傳參考是C++才有的東西,C語言是沒有的唷,可以說call by reference是call by address的進化版,
因為傳址的指標它的內容為指向的位址,但他本身仍然有記憶體位址,但是傳參考是不會有的
我們先來看程式碼
void swap (int &c , int &d){
int temp=c;
c=d;
d=temp;
}
int main(){
int a=5,b=10;
swap(a,b);
printf(" %d %d ", a,b);
return 0;
}
|
當主程式執行後記憶體如下
main的a | main的b | ||||
儲存值 | 5 | 10 | …… | …… | …… |
記憶體位址 | 0x04 | 0x08 | …… | …… | …… |
在呼叫swap(a,b)時像平常一樣直接給引數就好,但副程式那邊必須要把c跟d加上&,變成void swap (int &c , int &d)
PS:有些人會認為傳參考的&跟上面在講傳址的&是一樣的,其實是不一樣的~
這樣就會是用傳參考的方式來接收主程式a跟b的值,此時記憶體如下所示:
main的a | main的b | swap的&c | swap的&d | ||
儲存值 | 5 | 10 | …… | 5 | 10 |
記憶體位址 | 0x04 | 0x08 | …… | 0x04 | 0x08 |
由上表可以得知,傳參考的意思其實簡單來說就是變數的別名、綽號,因此c的記憶體位址跟主程式的a的位址一樣
因為一樣,所以c做任何改變a也會跟著改變,d改變b也會跟著改變
注意的是,一旦變數已經是別的變數的參考,就不可以再參考別的變數
int a=5,b=10;
int &c=a;
printf("c=%d \n",c);
&c=b; //錯誤,因為他已經參考了a
|
int a=5;
int &c; //錯誤,傳參考必須在宣告的時候就一起給參考的對象,沒辦法之後再給。
|
====================================================================
C++的call by reference,其實才算是call by address,來看下面的例子
void swap (int &c , int &d){
int temp=c;
c=d;
d=temp;
}
int main(){
int a=5,b=10;
const int x=20;
swap(a,b); //成功交換,因為a跟b的位址的值在副程式被別名c與d交換了
swap(a,x); //編譯錯誤,因為x為常數
swap(a,30); //編譯錯誤,因為30為常數
printf(" %d %d ", a,b);
return 0;
}
|
==========================
感謝網友修正以下程式碼
const int *x=&a; 是指到的值不能被改變 所以還是會compiler error
應該改成int * const x=&a; 才是你要的資料
Const 簡易補充說明
(1) pContent指針本身為常量不可變
(char*) const pContent;
const (char*) pContent;
(2) pContent的內容為常量不可變
const (char) *pContent;
(char) const *pContent;
(3) 兩者都不可變
const char* const pContent;
|
void swap (int *c , int *d){
int temp=*c;
*c=*d;
*d=temp;
}
int main(){
int a=5,b=10;
int * const x=&a;
int * const y=&b;
swap(x,y);
//因為傳址實際上是傳值而已,而那個值剛好就是address
//所以x跟y可以順利交換,雖然x跟y是const,但const是把值變常數
//然後指標存的是『位址』,因此不能改變的是『位址』,但位址的其值仍可以改變
printf(" %d %d ", *x,*y);
return 0;
}
|