一、值傳遞和指標傳遞的概念(一)值傳遞
值傳遞,即按值傳遞參數,按值傳遞參數時,是將實參變數的值複製一個到臨時儲存單元中,如果在調用過程中改變了形參的值,不會影響實參變數本身,即實參變數保持調用前的值不變。
1、形參只能是變數,實參可以是常量、變數或運算式。在被定義的函數中,必須指定形參的類型。
2、實參與形參的個數應一樣,類型應一致。字元型和整型可以互相通用。
4、實參傳遞給形參是實參的值,形參變數在未出現函數調用時,並不佔用記憶體,只在調用時才佔用。調用結束後,將釋放記憶體。值傳遞過程中參數的資料傳遞是單向的,資料(實參的值)從實參傳遞形參,而不能由形參傳回實參。執行一個被調用函數時,形參的值如果發生改變,並不會改變主調函數中的實參的值。
4、形參如同公式中的符號,實參就是符號具體的值,在調用過程前必須得到賦值;調用過程就是實現形參與實參的結合,把實參的值通過調用傳遞給形參,相當於把值代入公式進行計算。值傳遞的本質就是運算式。
(二)地址傳遞
地址傳遞,即按地址傳遞參數,按地址傳遞參數時,把實參變數的地址傳送給被調用過程,形參和實參共用記憶體的同一地址。在被調用過程中,形參的值一旦改變,相應實參的值也跟著改變。
1、實參必須是是變數,也就是保證可以重新被賦值或者初始化。在被定義的函數中,必須指定形參的類型。
2、實參與形參的個數應一樣,類型應一致。字元型和整型可以互相通用。
3、實參傳遞給形參的是實參變數的地址,函數調用過程中,並不為形參開闢儲存空間,也就是說地址傳遞過程中形參和實參共用實參的儲存空間,對形參的操作就是對實參本身的操作。
(三)值傳遞,地址傳遞的區別
1、值傳遞實參向形參傳遞的是實參的值,而地址傳遞傳遞的卻是實參的地址。
2、值傳遞在函數調用過程中會為形參重新開闢空間,形參與實參分別佔用不同的地址空間,而地址傳遞,形參和實參共用同一記憶體位址。
我們在參數傳遞過程中,只要抓住這兩點區別,就很好區別參數傳遞的具體方式。
二、C語言函數參數的傳遞規律
下面我們將從形參和實參是普通類型變數、指標變數、數組名時分別討論參數的傳遞方式。
(一)普通類型變數的參數傳遞
簡單類型變數作實參,形參對應為類型一致的簡單類型變數,請看下面的程式
void fun_of_value(int par_value){printf("In function, the Address of the VALUE = %p\n", &par_value);printf("In function, the Value of the VALUE = %d\n\n\n", par_value);}int main(void){int arg_value = 10;printf("In main, the Address of the VALUE = %p\n", &arg_value);printf("In main, the Value of the VALUE = %d\n", arg_value);fun_of_value(value);printf("After function, the Address of the VALUE = %p\n", &arg_value);printf("After function, the Value of the VALUE = %d\n", arg_value);}
我們在主程式和函數中分別列印實參arg_value和形參par_value的地址和值,下面是程式在Code::Block12.11中的運行結果
In main, the Address of the VALUE = 0028feecIn main, the Value of the VALUE = 10In function, the Address of the VALUE = 0028fed0In function, the Value of the VALUE = 10
簡易的記憶體配置圖示如下:
很顯然在參數傳遞的過程中,實參arg_value和形參par_value具有相同的值,但是佔用的儲存單元不相同,在函數中對形參的運算,對實參沒有影響,說明簡單類型變數在參數傳遞過程中是單向值傳遞的。
(二)指標變數的參數傳遞1、普通指標的作為參數
void fun_of_point(int *par_point){printf("In function, the Address of the POINT = %p\n", &par_point);printf("In function, the Value of the POINT = %p\n\n\n", par_point);}int main(void){int value = 10;int *arg_point = &value;printf("The Addreess of the value = %p\n", &value);printf("In main, the Address of the POINT = %p\n", &arg_point);printf("In main, the Value of the POINT = %p\n", arg_point); // 列印參數的值fun_of_point(point);return EXIT_SUCCESS;}
同樣我們在主程式和函數中分別列印實參arg_value和形參par_value的地址和值,下面是程式在Code::Block12.11中的運行結果
The Addreess og the value 0028feecIn main, the Address of the POINT = 0028fee8In main, the Value of the POINT = 0028feecIn function, the Address of the POINT = 0028fed0In function, the Value of the POINT = 0028feec
對結果分析後的圖示如下:
指標儲存的值其實是變數value的地址,我們發現,當參數是普通指標類型的時候,形參和實參都儲存著變數value的地址,也就是值相同,但是他們本身所佔用的儲存單元卻不相同,即不共用儲存空間,我們不難得出當參數類型是普通指標變數時,參數傳遞也是值傳遞,只是傳遞的值是地址(實參的值),並不是“地址傳遞”。
2、函數指標的參數
void function(void){}void fun_of_fun_point(void(*par_pfun)(void)){printf("In function, the Address of the FUNCTION = %p\n", &par_pfun);printf("In function, the Value of the FUNCTION = %p\n\n\n", par_pfun);}int main(void){void (*arg_pfun)(void);arg_pfun = /*&*/function;printf("The Address of the FUNCTION = %p\n", function);printf("In main, the Address of the FUNCTION = %p\n", &arg_pfun);printf("In main, the Value of the FUNCTION = %p\n", arg_pfun);fun_of_fun_point(arg_pfun);return EXIT_SUCCESS;}
運行結果如下:
The Address of the FUNCTION = 004016c9In main, the Address of the FUNCTION = 0028feecIn main, the Value of the FUNCTION = 004016c9In function, the Address of the FUNCTION = 0028fed0In function, the Value of the FUNCTION = 004016c9
對結果分析後的圖示如下:
與前面採取的方式類似,以函數指標作為參數與普通指著一樣,實參與形參都儲存了原函數的地址,但是他們本身的所佔用的儲存單元並不相同,可見此時參數傳遞方式仍然是值傳遞,傳遞的值是函數function的地址。
(三)數組名的參數傳遞
void fun_of_array(int array[]){printf("In function, the Address of the ARRAY = %p\n", &array);printf("In function, the Value of the ARRAY = %p\n", array);}void fun_of_array_point(int *array){printf("In function, the Address of the ARRAYPOINT = %p\n", &array);printf("In function, the Value of the ARRAYPOINT = %p\n", array);}int main(void){int array[10];printf("The Addres of the first value %p\n", &array[0]);printf("In main, the Address of the ARRAY = %p\n", &array);printf("In main, the Value of the ARRAY = %p\n", array);fun_of_array(array);fun_of_array_point(array);return EXIT_SUCCESS;}
程式的運行結果如下:
The Addres of the first value 0028fec8In main, the Address of the ARRAY = 0028fec8In main, the Value of the ARRAY = 0028fec8In function, the Address of the ARRAY = 0028feb0In function, the Value of the ARRAY = 0028fec8In function, the Address of the ARRAYPOINT = 0028feb0In function, the Value of the ARRAYPOINT = 0028fec8
我們不難看出,數組的地址和“數組名的值”(我們暫且這樣稱呼)都儲存了數組的首地址,其實大多數編譯器(如gcc)在處理的時候會在預先處理階段把數組名類型的參數轉換為指標類型的參數進行處理的,在編譯器看來,在以數組名作為參數的時候數組參數本質上為指標參數,
因此以數組名作為參數,本質上也是指標類型的參數傳遞,也採用單向值傳遞的方式。
三 總結
由此我們不難得出結論,
注意:引用傳遞是C99新引入的參數傳遞機制,但是目前為止,仍沒有一款編譯器完全支援C99標準,因此引用傳遞在此不進行討論,但是通過對資料的研究,其實引用傳遞本質也是採用指標傳遞的機制來實現的。