[問題] 請問local變數在離開stack後為何能存取

看板C_and_CPP (C/C++)作者 (動き出す時間...)時間3年前 (2021/04/01 21:54), 3年前編輯推噓7(7024)
留言31則, 9人參與, 3年前最新討論串1/1
開發平台(Platform): (Ex: Win10, Linux, ...) win10/debian 編譯器(Ex: GCC, clang, VC++...)+目標環境(跟開發平台不同的話需列出) VC2008/gcc 程式碼(Code):(請善用置底文網頁, 記得排版,禁止使用圖檔) #include <stdio.h> struct StructB { int m_nB; StructB() { m_nB = 0; } ~StructB() { printf("~StructB()\n"); } }; struct StructA { StructA() { m_pB = NULL; } StructB *m_pB; }; void foo(StructA &p_stA) { StructB stB; stB.m_nB = 1; p_stA.m_pB = &stB; } int main() { StructA stA; foo(stA); printf("stA.m_pB->m_nB = %d\n", stA.m_pB->m_nB); printf("stA.m_pB->m_nB = %d\n", stA.m_pB->m_nB); // 連續兩次看結果 return 0; } 補充說明(Supplement): 今天工作上我看到project code有很明顯的問題如下. 有一個struct其中一個member是一個pointer, 這個struct產生一個member object放在一個class裡面. 而我尋找這個pointer並沒有任何地方去new物件出來, 而是直接在一個function裡面產生一個local變數, 然後把local變數的位址設定給這個pointer. 問題來了, 設定local變數給這個struct的pointer, 然後離開這個function回到上一層stack, local變數不就free掉了嗎? 再去存取這個struct pointer不是應該就會出問題? 但結果沒有, 我用VC debug看程式竟然還能存取到正確的值. 這讓我對以前變數lifecycle學習產生了質疑, 因此我直接寫了上方簡單的程式碼來驗證這件事. 我分別用VC以及gcc在win10/debian底下去執行上面的程式碼, 神奇的事發生了 win10: 第一次printf看到印出的值正確為1, 第二次變為-2. debain: 兩次都為1. 我確認StructB的解構式已經"先"印出來才印出數值, 為何被free的變數還能存取到他的數值呢? 請版上先進指教, 謝謝。 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 1.163.153.38 (臺灣) ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1617285280.A.419.html

04/01 21:57, 3年前 , 1F
你運氣好(不好)
04/01 21:57, 1F

04/01 21:57, 3年前 , 2F
就只是單純當時分給他的位置還沒人來而已
04/01 21:57, 2F

04/01 21:58, 3年前 , 3F
寫出 UB 就是爽,一直寫 UB 一直爽
04/01 21:58, 3F
undefined behavior? ※ 編輯: Keitaro (1.163.153.38 臺灣), 04/01/2021 22:01:37

04/01 22:37, 3年前 , 4F
是 undefined behavior 沒錯,不過你這個例子,compile
04/01 22:37, 4F

04/01 22:37, 3年前 , 5F
r 應該會報 warning / error 吧?
04/01 22:37, 5F
warning我沒仔細看明天確認一下, error當然是沒有的 不然就無法執行了

04/01 22:56, 3年前 , 6F
並沒有free掉 ,stack pointer 和base pointer回到上一
04/01 22:56, 6F

04/01 22:56, 3年前 , 7F
個function的狀態,你在callee之後宣蹷@個大的陣列即
04/01 22:56, 7F

04/01 22:56, 3年前 , 8F
可把stack memory洗掉。
04/01 22:56, 8F
這樣的話 那我想再請教一下關於解構式的意義了 我以為會跑解構式就已經是把這個物件準備從memory中移除了耶 以new的方式來看 用上面相同的程式碼 我如果寫 StructB *pB = new StructB; delete pB; 在delete pB之後在VC裡面debug看m_nB的數值會是???? 表示無法存取的狀態 請問靜態宣告local變數跟動態new變數 在free跑完解構式的差異是? ※ 編輯: Keitaro (1.163.153.38 臺灣), 04/01/2021 23:32:52

04/01 23:57, 3年前 , 9F
差別是放在 stack 上還說 heap 上
04/01 23:57, 9F
我懂了 您的意思是說在stack上的需要等這個stack被洗掉才會真正release是嗎?

04/01 23:57, 3年前 , 10F
如果有 address sanitizer 可以用的話開起來應該也可以
04/01 23:57, 10F

04/01 23:57, 3年前 , 11F
在執行時跳出錯誤
04/01 23:57, 11F
感恩 來去學一下 謝謝 ※ 編輯: Keitaro (1.163.153.38 臺灣), 04/02/2021 00:14:47

04/02 00:19, 3年前 , 12F
嚴格說起來是你的rsp/rbp改掉的時候就算release了
04/02 00:19, 12F

04/02 00:21, 3年前 , 13F
只是release不代表你的local variable值一定會改變
04/02 00:21, 13F
我想您的意思應該是說當rsp/rbp改掉的時機是指 local variable解構式跑完->stack回到上一層 這個階段 但這個stack在記憶體的位置並沒有被free掉 只是rsp/rbp改成上一層stack而已 所以我去存取這個local variable還能存取到他的數值 如果我想法是正確的 是否我改成這樣 void foo2() // 不做任何事 {} int main() { StractA stA; foo(stA); foo2(); return 0; } 是否這樣foo()的stack就會被洗掉了? stA.m_pB->m_nB 再也無法存取? ※ 編輯: Keitaro (1.163.153.38 臺灣), 04/02/2021 00:37:31

04/02 00:33, 3年前 , 14F
有沒有洗掉不是很重要,就算他沒被洗掉你也不應該去用
04/02 00:33, 14F

04/02 00:33, 3年前 , 15F
,誰知道等下還會不會留著
04/02 00:33, 15F
我了解 只是我想知道compiler到底是偷做了甚麼事顛覆了我對變數lifecycle的認知這樣 我當然是不敢這樣去用上一個stack的local variable的. ※ 編輯: Keitaro (1.163.153.38 臺灣), 04/02/2021 00:44:06

04/02 01:52, 3年前 , 16F
你對free跟lifecycle消滅的定義是什麼呢?
04/02 01:52, 16F

04/02 01:53, 3年前 , 17F
是執行期當你access這塊記憶體時 程式應該要報錯嗎?
04/02 01:53, 17F
是的 我以為lifecycle結束就跑解構式 已經跑過解構式的物件 在記憶體上就已經被刪除 既然被刪除就應該是無法存取了 我一直以為是這樣

04/02 02:31, 3年前 , 18F
Compiler 沒偷做什麼,頂多說是偷懶沒把 stack 刷掉
04/02 02:31, 18F

04/02 04:14, 3年前 , 19F
為什麼刪除的檔案還有機會找回來?因為剛好沒被洗掉。
04/02 04:14, 19F

04/02 04:14, 3年前 , 20F
為什麼上一個function裡面local variable還在?因為剛
04/02 04:14, 20F

04/02 04:14, 3年前 , 21F
好沒被洗掉
04/02 04:14, 21F

04/02 04:17, 3年前 , 22F
compiler沒有偷做什麼,他只是不再使用這個地方而已。
04/02 04:17, 22F

04/02 04:17, 3年前 , 23F
既然已經不再使用這裡也就不用特地清成0了
04/02 04:17, 23F
原來如此 所以讀到那個1真的只是運氣好而已

04/02 08:17, 3年前 , 24F
先執行destructor ,再離開當下function (做stk /bsp re
04/02 08:17, 24F

04/02 08:17, 3年前 , 25F
store) ,最後pop pc+1 與jump ;destruct內沒有清除data
04/02 08:17, 25F

04/02 08:17, 3年前 , 26F
member的行為,對吧
04/02 08:17, 26F

04/02 13:02, 3年前 , 27F
或者應該反過來說, compiler 就是沒偷做什麼才會還能讓你
04/02 13:02, 27F

04/02 13:03, 3年前 , 28F
偷偷回去挖裡面原來放了些什麼東西
04/02 13:03, 28F

04/02 13:04, 3年前 , 29F
ie.這裡沒照規矩做事的反而是程式去存取已經結束生命的變數
04/02 13:04, 29F
了解了 非常感謝以上各位的說明 讓小弟我又學到不少 謝謝! ※ 編輯: Keitaro (1.163.153.38 臺灣), 04/02/2021 13:45:36

04/03 03:43, 3年前 , 30F
在這串學到許多+1 謝謝板上各位大大!
04/03 03:43, 30F

04/06 03:11, 3年前 , 31F
特別去清掉會造成performance hit 沒甚麼必要
04/06 03:11, 31F
文章代碼(AID): #1WPT2WGP (C_and_CPP)
文章代碼(AID): #1WPT2WGP (C_and_CPP)