1.首先,我們要明白執行函數時,編譯器對參數以及返回值的處理。編譯器會給每個函數參數創建一個臨時副本;對於函數返回值,編譯器在執行return語句時,會創建一個臨時副本,並將該副本放入緩衝區中。
2.下面我們將結合圖和代碼演示來分析函數在“值傳遞“和”地址傳遞“時,發生的動作。
值傳遞
我們給出這樣一段代碼片段:
int byvalue(int temp)
{
temp++;
int temp01 = 0;
temp01++;
return temp01;
}
int main()
{
int a = 2;
int b = 2;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
b = byvalue(a);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
我們按照1中知識,我們知道在執行byvalue函數時,編譯器會創建參數temp的一個臨時副本_temp,在函數體內一切對temp的操作,其實都是對其臨時副本_temp的操作。所以對於操作”temp++”其實是”_temp++”,修改的是_temp的值,而temp的值不變,我們可用圖1形象表示。
那麼,對於返回值呢?又是怎樣的情況呢。
同樣,從1中我們知道,在執行return temp01時,函數會創建temp01的副本_temp01並將_temp01存在緩衝區中,並且在離開函數作用域後,temp01的生命週期結束,內存被釋放,但緩衝區中的_temp01還存在着,如圖2所示。
執行上述代碼片段,實參a的值在執行byvalue函數之後不會改變,但函數返回值會賦值給b,b會改變。我們可以得到以下結果:
所以函數”值傳遞“並不會改變實參,但是對返回值卻是有效的。
地址傳遞
同樣,我們也給出一段代碼,並對代碼進行分析。
int* bypointer(int *p)
{
//注意*p++和(*p)++的區別,前者是指針自增1後,取指向值;後者是取p指向的值,並該值自增1
(*p)++;
int temp01 = 1;
temp01++;
int *q = &temp01;
return q;
}
int main()
{
int a = 1;
int *a_p = &a;
cout << *a_p << endl;
int *b_p;
b_p = bypointer(a_p);
//byvalue(a);
cout << *a_p << endl;
cout << *b_p << endl;
return 0;
}
對於指針傳遞,我們同樣可以按照值傳遞的思路分析。首先,在執行函數bypointer()函數時,函數會給指針p一個臨時副本_p。我們可以知道指針中存的是某段內存的地址,所以我們知道p和_p中有相同的值,是同一內存段的首地址,即p和_p指向同一內存。在bypointer函數體內執行(*p)++,也就是(*_p)++,不過p和_p指向同一內存,*p和*_p指代同一變量,所以(*p)也確實自增了1,如圖4所示。
對於”指針傳遞“的返回值,這裏我們需要考慮兩種情況:1)指針指向棧內存;2)指針指向非棧內存(堆、全局或靜態存儲區、常量存儲區)
同”值傳遞“返回值一樣分析:
在執行return q;
語句時,創建指針q的副本_q,並存在緩衝區中。但若q指向的是棧內存的話,在函數結束後,這塊內存會被釋放,_q也就成爲指向垃圾內存的野指針,自然將其賦值給b_q後,b_q也是指向垃圾內存的野指針,示意如圖5所示。
執行上述代碼片段,指針a_p指向的值在執行byvalue函數之後會改變,但函數返回的指針指向的棧內存已被釋放,故b_p指向的值爲垃圾值。我們可以得到以下結果:
所以,函數返回值不能是指向”棧內存“的指針,但可以是指向”堆內存“的指針,因爲函數結束時,編譯器不會自動釋放堆內存。(注意char *p = “hello world!”,這裏的p是指向常量內存區的指針,函數返回這樣的指針也是可以的,不過返回的始終是一個只讀的內存塊。)
Note:棧內存中的變量通常是局部變量、函數參數等。