c++ 函數返回類型有三種: 1.返回值; 2.返回指針; 3.返回引用.
下面介紹在函數返回的時候到底發生了什么動作。
1.返回值類型:
一個簡單的例子:
int get()
{
int n=3;
return n;
}
int main()
{
int k=get();
}
先看get函數,局部變量n是產生在棧區的值,因此在函數結束時空間就會被釋放,這個函數結束就底是指
在return前還是return后呢。
答案是不一定,一般是不會立刻被釋放。
return這個動作會把n的值復制到一個臨時變量q,這個臨時變量充當函數的返回值,直到main函數的賦值成功后臨時變量就被銷毀。
所以實際上在main函數里面 k=get(), k是獲取get()函數產生的臨時變量的值,而這個值的確是存在的,所以賦值成功。
在k成功獲取q的值之后,臨時變量q和局部變量n可能會被銷毀,由于寫時復制的存在,也可能不會立刻被銷毀,這是不確定的。
2.返回指針類型:
int *get()
{
int *p = new int(3);
return p;
}
int main()
{
int *k=get();
}
先看get函數,局部變量p申請了一塊動態內存,值為3. 在return p這里發生的動作有:
函數會產生一個臨時變量,這個變量類型是int*,我們假設為 q。
q = p; 即q指向了p指向的地方,這里是p申請的那塊動態內存。
臨時變量q充當函數的返回值。
所以在main函數里面 k = get() 實際上就是 k = q,這時候k就指向了那塊動態申請的內存。
同理臨時變量q完成任務后和局部變量p的地址是無效的,但它們的值(指向的地方)仍然存在。
所以返回一個指向動態內存的局部指針是可行的。
錯誤用法:返回指向局部對象的指針。
int *get()
{
int n = 3;
return &n;
}
int main()
{
int *p = NULL;
int *k = NULL;
p = k = get();
cout<<*p<<endl;
cout<<"operation"<<endl;
cout<<*p<<endl;
}
這里get函數返回的是n的地址。
在get函數返回的時候同樣發生了一次復制,假設臨時變量名為temp ,int *temp = &n;
此時temp的確指向了n的地址,并作為函數的返回值返回。
再看main函數 這里我用了一個鏈式復制來更好說明問題。
先看 k = get(),即發生了 k =temp; 此時k也指向了函數n的值。
然后p = k,p也指向了函數局部變量n的值。
cout<<*p 神奇地發現的確輸出3. 可能大家會想難道局部變量n的值還沒被釋放么。
的確還是沒被釋放,因為n是臨時變量,n的地址是被系統回收了
但它的值還沒有被別的內容寫入,所以3這個值仍然存在。 之所以這里設計是涉及到一個編譯器運用內存的效率,即寫時復制,當需要這塊內存時候直接寫入省去中間釋放這個步驟。
但在第二次執行 cout<<*p<<endl; 發現不再輸出3而是一個值很大的數。
這時候說明3這個值占用的內存被重寫了。 所以解釋是:在第一個cout<<*p<<endl執行之后,當再運行一段時間后,系統在調用<<函數、輸出結果等動作中進行了內存回收,此時該地址對應的值就被更改成無效的值了。
所以這是一個不確定的問題,你不知道編譯器什么時候會把這個臨時變量n的值釋放掉,所以存在安全隱患。
返回局部引用也是如此。
文章列表