文章程式碼顯示

2017年11月15日 星期三

《筆記》C語言 - 06_2:傳參考呼叫(傳址呼叫)、加入 const 來呼叫陣列

傳參考呼叫(傳址呼叫)

先前我們提到對於一般變數的函式呼叫,我們使用的是「傳值呼叫」(見《筆記》C語言 - 05:函式基本、傳值呼叫 文末),傳值呼叫會將原本的變數產生一個複製品後,交由函式來進行調用,並不會更改到原參數的值

前一章我們又有提到一個觀念,「陣列中的各別元素可以視為一個變數」,也就是說當我們用「函式對陣列中的某一個元素進行呼叫」時,陣列中的元素會以「傳值呼叫」來進行傳遞

如果要傳遞整個陣列則不同

陣列會以模擬的傳參考呼叫(call by reference)來進行傳遞(詳細概念於指標章節說明會更清晰,於此章節想表達的是另一件事)

2018/09/08 更新 :
事實上模擬的傳參考呼叫(call by reference)這是中譯本所述,在其它的書籍中也有將此傳遞方式稱為 傳址呼叫(call by address) ,但傳址呼叫似乎不是很正規的說法,更為正規的說法是 call by value of pointer ,但這在此章說明並不適合(因為我還沒提到有關於指標(pointer)的觀念)

總之,如物理學家費曼的爸爸對費曼所說 : 「理查(費曼),你知道這鳥的名稱嗎?我可以告訴你這鳥在不同語言中的各種名稱,但其實除了發音外,對於那隻鳥你其實什麼也不知道。我們不如來細心看看這隻鳥類的生活習性,例如牠的身體外形、特徵、吃什麼等等。」

名稱不重要,重要的是你知道它到底在做什麼


#include "stdio.h"

#define SIZE 5

void modify_Array( int b[], int size);

int main(void) {

 int a[SIZE] = { 1, 2, 3, 4, 5 };
 int i;

 printf( "Original array a[]\n" );

  for ( i = 0; i < SIZE ; i++){
   printf( "%d ", a[i] );
  }

 printf( "\n\n===== Call by reference =====\n\n" );


 modify_Array( a, SIZE);

 printf( "\n\na[] After modify_Array : " );
 for ( i = 0; i < SIZE ; i++){
   printf( "%d ", a[i] );
  }
}

void modify_Array( int b[], int size){
 int j,k;

 for( j = 0; j < size; j++){
  b[j] = b[j] + 3;
 }

 printf( "a[] In modify_Array : " );

 for( k = 0; k < size; k++){
  printf( "%d ", b[k] );
 }
}



傳值呼叫(call by value) 會對原本的變數建立一個複製品,但以模擬的傳參考呼叫(call by reference)傳址呼叫(call by address)則會直接更改來源

由上面的程式碼中已經可以看出,我們在 modify_Array 裡面對 main 裡的區域陣列 a[] 進行運算並且把值在函式裡面顯示列印出來,當函式結束並且將控制權交回函式呼叫後,在 main 函式裡面再一次顯示列印出區域陣列 a[]。

結果發現在 main 裡面的區域陣列 a[] 被更改了,證明將整個陣列經由函式呼叫會以模擬的傳參考呼叫(call by reference)或稱傳址呼叫(call by address)傳遞,並不會像傳值呼叫(call by value) 一樣產生出一個複製品,而是直接更改在 main 函式內的原區域陣列 a[]

#include "stdio.h"

#define SIZE 5

void modify_Element(int c);

int main(void) {

 int a[SIZE] = { 1, 2, 3, 4, 5 };
 int i;

 printf( "Original array a[]\n" );

  for ( i = 0; i < SIZE ; i++){
   printf( "%d ", a[i] );
  }

 printf( "\n\n===== Call by value(array's element) =====\n\n" );


 modify_Element(a[2]);

 printf( "\na[] after modify_Element : " );
 for ( i = 0; i < SIZE ; i++){
   printf( "%d ", a[i] );
  }
}

void modify_Element(int c){

 c = c * 20;
 printf( "a[2] in modify_Element : %d\n", c );
}



如果我們將陣列中的某一個元素,經由函式呼叫傳遞,則形同一般整數為傳值呼叫。

將 main 函式裡面的區域陣列 a[2] 的數值 3 經由函式呼叫傳遞至 modify_Element 函式,並且在裡面進行值的更改。然後在 main 裡面再次列印顯示整個區域陣列 a[] ,發現陣列經過函式的更改後,原始陣列並不會被改變

加入 const 來呼叫陣列

為免文章冗長,後續文章將 模擬的傳參考呼叫(call by reference) 一律以 傳址呼叫(call by address) 進行表示

由上例可知,陣列會以傳址呼叫來傳遞,因此你很難控制函式呼叫不去更改原陣列的值

但我們可以利用型別修飾詞 const 來避免呼叫函式更改陣列的值。當一個陣列呼叫在函式標頭的前面加上 const 修飾詞之後,陣列的元素在函式的本體當中會成為唯讀常數。所以在函式本體內任何試圖更改陣列值的動作,都會讓編譯器報錯。

#include "stdio.h"

int main(void) {
 int a[] = { 5,6,7,8};

 tryToModifyArray( a );
}

void tryToModifyArray(const int b[]){
 b[0] = b[0] +5;
}


注意,這裡的 const 是加在函式標頭以及原型內,並不是在宣告陣列的地方。

↓↓↓ 連結到部落格方針與索引 ↓↓↓

Blog 使用方針與索引