學習筆記六:函數-值傳遞和地址傳遞的分析

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

那麼,對於返回值呢?又是怎樣的情況呢。
同樣,從1中我們知道,在執行return temp01時,函數會創建temp01的副本_temp01並將_temp01存在緩衝區中,並且在離開函數作用域後,temp01的生命週期結束,內存被釋放,但緩衝區中的_temp01還存在着,如圖2所示。

圖2

執行上述代碼片段,實參a的值在執行byvalue函數之後不會改變,但函數返回值會賦值給b,b會改變。我們可以得到以下結果:

圖3

所以函數”值傳遞“並不會改變實參,但是對返回值卻是有效的。

地址傳遞
同樣,我們也給出一段代碼,並對代碼進行分析。

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所示。

圖4

對於”指針傳遞“的返回值,這裏我們需要考慮兩種情況:1)指針指向棧內存;2)指針指向非棧內存(堆、全局或靜態存儲區、常量存儲區)
同”值傳遞“返回值一樣分析:
在執行return q;語句時,創建指針q的副本_q,並存在緩衝區中。但若q指向的是棧內存的話,在函數結束後,這塊內存會被釋放,_q也就成爲指向垃圾內存的野指針,自然將其賦值給b_q後,b_q也是指向垃圾內存的野指針,示意如圖5所示。

圖5

執行上述代碼片段,指針a_p指向的值在執行byvalue函數之後會改變,但函數返回的指針指向的棧內存已被釋放,故b_p指向的值爲垃圾值。我們可以得到以下結果:

圖6

所以,函數返回值不能是指向”棧內存“的指針,但可以是指向”堆內存“的指針,因爲函數結束時,編譯器不會自動釋放堆內存。(注意char *p = “hello world!”,這裏的p是指向常量內存區的指針,函數返回這樣的指針也是可以的,不過返回的始終是一個只讀的內存塊。)
Note:棧內存中的變量通常是局部變量、函數參數等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章