書上都說不能返回區域性變數的引用或區域性指標,說這種行為危險,但又沒講具體原因,那麼今天就來看看這種行為的具體細節
ps:下面含有at&t彙編內容,未學過彙編的朋友可以跳過直接看結論先放乙個實驗用函式,即返回
int&
型別的區域性變數的函式
int
&retint()
稍後會用到的幾個測試函式,用來說明不同情況下使用區域性的結果
void
nop(
)void
snop()
void
skip()
主函式如下(待會使用控制變數法)
呼叫nop
函式
/**
* @brief filename:main.cpp
* environment : g++&ubuntu18.04
*/int
main()
$ g++ main.cpp -omain;./main #編譯指令
輸出結果為:即兩次使用返回的區域性變數引用都表現正常4242
這時候回顧前瞻知識:
區域性變數存放在棧區
區域性變數使用後會被銷毀(棧頂指標變化)
引用為變數別名,實際編譯中仍會取變數位址(可見下面源程式和彙編相應部分)
int i=42;
int& iref=i;
int* ip=
&i;
movl $42, -36(%rbp) ;int i=42;
leaq -36(%rbp), %rax ;leaq 為彙編取位址操作
movq %rax, -32(%rbp) ;int& iref=i;//可見引用佔的大小和 型別(int)大小是相同的
leaq -36(%rbp), %rax
movq %rax, -24(%rbp) ;int* ip=&i;//指標為 8 個 位元組
解釋
引用存放位址不與變數存放位址相同
銷毀物件並不會改變相應位址的值,只改變棧頂指標,所以值能很容易被重寫,但目前沒有資料去重寫這塊位址的值
連續宣告兩個變數的位址在之後(即沒有覆蓋)
下面看一下彙編的具體細節
main函式
黃框部分為c++原始檔中函式語句直接對應部分
可以看到之前函式所用記憶體:-20(%rbp) -16(%rbp) 是沒有改變的
呼叫snop
函式
編譯指令不變
輸出結果:
這時似乎可以懷疑是int i=retint();
時,這時的 i 已經是3了
那麼修改了一下main函式
結果不變,和上面是一樣啊
到這裡就基本驗證了之前的解釋
只宣告變數的函式未覆蓋之前銷毀的物件位址
而如果使用了引用則覆蓋位址
呼叫skip
函式
編譯指令不變
輸出結果:
421074842828
將skip
函式中常量引用 d 的值改為 0.0 後,輸出第二行為 0
如果第二次實驗只是改變了數值,還勉強能看到值的由來的話,那這個 4.2 帶來的值的變化,是著實看不懂了
其實的話,是用double型別浮點數的形式儲存 4.2 然後用int型別的形式去讀取相應記憶體單元的結果
可以看出顯示的是skip函式定義的常量引用的高位位址的值
也許會有人質疑double型別的會影響是因為double 八位,相當於 int 與 int& 的總和
於是對skip
函式稍加更改
輸出結果:
42retint 函式的彙編沒有變化4
可以看到依然發生了對位址的覆蓋改寫
實驗結果是對書所說結果的驗證。
銷毀物件的意思是可以改寫那一塊的值。
區域性變數引用的使用是有很大風險的
1. 風險在於所使用的引用的值,可能發生意味的改變使用者不知道或未注意到的
2. 除非是當臨時值使用 如同std:cout<
其實看著彙編**來說的話 原來那個位置的值還是可以算出來的 能通過看**最後知道那個值
1. 不過 正經人誰做這個
簡單點說就是 理解這種情況就相當於
int
retref()
intmain()
補充一點就是 返回指向區域性變數的指標跟這個是差不多 不能返回區域性變數的引用
源之 int add1 int a,int b int add2 int a,int b 請問這兩個函式返回有什麼區別,是乙個返回副本,另乙個直接返回嗎?呼叫函式add2有什麼危險嗎?add1的確返回了乙個副本,如果sum是自定義的類型別,可以很明顯看出拷貝建構函式在返回時被呼叫,對於內建型別沒什麼...
千萬不能返回區域性變數的引用??
c primer第7章函式一節,講到返回時,理解返回引用至關重要的是,千萬不能返回區域性變數的引用 意思是返回程式內部定義的變數時可能會出問題,因為當函式執行完畢後,將釋放分配給區域性物件的儲存空間。此時,對區域性物件的引用就會指向不確定的記憶體。覺得不能理解。比如求階乘時,可以使用迭代函式的方法,...
引用與區域性變數的返回問題
今天剛相對徹底的搞懂函式返回區域性普通變數 不包括指標和引用 與區域性指標 引用的返回區別,先看下面 include include using namespace std string version1 const string s1,const string s2 const string ve...