这是我的代码
#include<iostream>
using namespace std;
int *ptr;
void hold(){
int a=12;
ptr=&a;
};
int main(){
hold();
cout<<"value of a="<<*ptr;
};
我得到a=12
的价值
这不应该发生,因为编译器已经释放了a的地址值
为什么12来了?
答案 0 :(得分:6)
这是未定义的行为。
你可以得到任何价值,或者崩溃,或者任何事情。
由于“正是我所期望的”是“未定义的行为”的一部分,你不能把它理解为“它做了我所期望的,因此它不是未定义但完美地工作”。这种推理无效。
答案 1 :(得分:3)
释放并不意味着系统将修改存储在那里的值(这将涉及不必要的开销)。它只是意味着这个存储空间可供将来存储。
答案 2 :(得分:1)
因为您正在调用未定义的行为。当你做你做的事情时,任何事情都可能发生。
更详细一点:在本地范围内定义的变量而不是“静态”驻留在一个区域中,C规范只是模糊地称为“自动内存”。只要保留函数的范围,就不再定义该函数调用的自动存储器中的所有内容。为清楚起见:自动内存与 函数无关,它与函数的特定调用相关联。
完成自动内存分配/解除分配的实用方法是实现定义的。但是实现自动内存有一种事实上的标准方法,称为堆栈帧。
堆栈是指定的内存区域,其中每个函数调用现有堆栈顶部的内存区域的另一个块被分配给函数调用的范围。因此,对于程序中函数链调用中的每个函数调用,将另一大块自动存储区放在先前分配的区域之上,即它形成一堆自动存储区。当剩下一个函数作用域 - 并且只能发生在函数调用链中的最后一个函数时 - 相关堆栈帧的位置就会被丢弃。但是在那里存储的内容并没有在通常的实现中被破坏。这意味着,只要没有其他功能被调用,你会发现留下的垃圾,如果你知道在哪里看。
所以在基于堆栈框架的实现中,没有在范围退出时进行清理,在自动内存中获取变量的地址并将其传递到更高的范围就可以了,并且可以向您展示不再有效范围的内容。但是实现可以有效地选择将堆栈存储器的未使用部分标记为OS无效并且尝试访问它也可能使程序崩溃。
答案 3 :(得分:1)
a
(因为它仅在函数的体范围内可见),因此ptr
指针指向未分配的内存地址。由于释放不会删除该地址的值(只是将地址标记为空闲),因此在访问该地址的ptr
时仍可能显示值12。
答案 4 :(得分:1)
函数中局部变量的地址取决于调用函数时执行点的堆栈状态(SP寄存器的值)。
因此,每次调用函数时,局部变量可能具有不同的地址,并且一旦您在函数外部,就不能依赖该地址的内容。
答案 5 :(得分:0)
我知道你的问题已经得到了很好的回答,但如果你有兴趣了解有关幕后发生的事情。 这样你就可以更清楚地了解这些东西为何以及如何运作,试试这个:
#include <iostream>
using namespace std;
int *ptr;
void hold(){
int a=12;
ptr=&a;
};
void func() {
b = 3444;
}
int main(){
hold();
func();
cout<<"value of a="<<*ptr;
};
您可能会发现打印出一个非常不同的值。正如barak所暗示的那样幕后发生的事情是a和b的值被分配在堆栈上。当函数返回时,变量“被释放”。它实际上并没有随着堆栈指针的改变而被释放。在随后的func()
调用中,另一个堆栈帧被简单地放在堆栈上。这个其他堆栈帧与hold()
的堆栈帧具有非常相似的内容,因此b
的位置可能与a
的位置相同。同样,我说机会是因为不需要实现以这种方式放置堆栈。
它的要点是,当你在堆栈上分配东西时,它会一直存在,直到它上面出现新的东西(另一个堆栈帧)。