先前我們提到對於一般變數的函式呼叫,我們使用的是「傳值呼叫」(見《筆記》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 是加在函式標頭以及原型內,並不是在宣告陣列的地方。