close

轉錄至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;
}
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 LiLun 的頭像
    LiLun

    LiLun の 分享平台

    LiLun 發表在 痞客邦 留言(0) 人氣()