作者Keitaro (动き出す时间...)
看板C_and_CPP
标题[问题] 请问local变数在离开stack後为何能存取
时间Thu Apr 1 21:54:38 2021
开发平台(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://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1617285280.A.419.html
1F:→ nh60211as: 你运气好(不好) 04/01 21:57
2F:推 LPH66: 就只是单纯当时分给他的位置还没人来而已 04/01 21:57
3F:→ Lipraxde: 写出 UB 就是爽,一直写 UB 一直爽 04/01 21:58
undefined behavior?
※ 编辑: Keitaro (1.163.153.38 台湾), 04/01/2021 22:01:37
4F:→ Lipraxde: 是 undefined behavior 没错,不过你这个例子,compile 04/01 22:37
5F:→ Lipraxde: r 应该会报 warning / error 吧? 04/01 22:37
warning我没仔细看明天确认一下, error当然是没有的 不然就无法执行了
6F:推 ando5566: 并没有free掉 ,stack pointer 和base pointer回到上一 04/01 22:56
7F:→ ando5566: 个function的状态,你在callee之後宣蹷@个大的阵列即 04/01 22:56
8F:→ ando5566: 可把stack memory洗掉。 04/01 22:56
这样的话 那我想再请教一下关於解构式的意义了
我以为会跑解构式就已经是把这个物件准备从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
9F:→ Lipraxde: 差别是放在 stack 上还说 heap 上 04/01 23:57
我懂了 您的意思是说在stack上的需要等这个stack被洗掉才会真正release是吗?
10F:→ Lipraxde: 如果有 address sanitizer 可以用的话开起来应该也可以 04/01 23:57
11F:→ Lipraxde: 在执行时跳出错误 04/01 23:57
感恩 来去学一下 谢谢
※ 编辑: Keitaro (1.163.153.38 台湾), 04/02/2021 00:14:47
12F:→ sarafciel: 严格说起来是你的rsp/rbp改掉的时候就算release了 04/02 00:19
13F:→ sarafciel: 只是release不代表你的local variable值一定会改变 04/02 00:21
我想您的意思应该是说当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
14F:推 b0920075: 有没有洗掉不是很重要,就算他没被洗掉你也不应该去用 04/02 00:33
15F:→ b0920075: ,谁知道等下还会不会留着 04/02 00:33
我了解 只是我想知道compiler到底是偷做了甚麽事颠覆了我对变数lifecycle的认知这样
我当然是不敢这样去用上一个stack的local variable的.
※ 编辑: Keitaro (1.163.153.38 台湾), 04/02/2021 00:44:06
16F:→ sarafciel: 你对free跟lifecycle消灭的定义是什麽呢? 04/02 01:52
17F:→ sarafciel: 是执行期当你access这块记忆体时 程式应该要报错吗? 04/02 01:53
是的 我以为lifecycle结束就跑解构式
已经跑过解构式的物件 在记忆体上就已经被删除
既然被删除就应该是无法存取了 我一直以为是这样
18F:→ Lipraxde: Compiler 没偷做什麽,顶多说是偷懒没把 stack 刷掉 04/02 02:31
19F:→ a1u1usul3: 为什麽删除的档案还有机会找回来?因为刚好没被洗掉。 04/02 04:14
20F:→ a1u1usul3: 为什麽上一个function里面local variable还在?因为刚 04/02 04:14
21F:→ a1u1usul3: 好没被洗掉 04/02 04:14
22F:→ a1u1usul3: compiler没有偷做什麽,他只是不再使用这个地方而已。 04/02 04:17
23F:→ a1u1usul3: 既然已经不再使用这里也就不用特地清成0了 04/02 04:17
原来如此 所以读到那个1真的只是运气好而已
24F:推 ando5566: 先执行destructor ,再离开当下function (做stk /bsp re 04/02 08:17
25F:→ ando5566: store) ,最後pop pc+1 与jump ;destruct内没有清除data 04/02 08:17
26F:→ ando5566: member的行为,对吧 04/02 08:17
27F:推 LPH66: 或者应该反过来说, compiler 就是没偷做什麽才会还能让你 04/02 13:02
28F:→ LPH66: 偷偷回去挖里面原来放了些什麽东西 04/02 13:03
29F:→ LPH66: ie.这里没照规矩做事的反而是程式去存取已经结束生命的变数 04/02 13:04
了解了 非常感谢以上各位的说明 让小弟我又学到不少 谢谢!
※ 编辑: Keitaro (1.163.153.38 台湾), 04/02/2021 13:45:36
30F:推 unmolk: 在这串学到许多+1 谢谢板上各位大大! 04/03 03:43
31F:推 steve1012: 特别去清掉会造成performance hit 没甚麽必要 04/06 03:11