繁体   English   中英

为什么在函数完成执行后释放局部变量的值(其地址存储在全局变量中)

[英]Why value of local variable(its address is stored in global variable) is freed when function has completed its execution

这是我的代码

#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来了?

这是未定义的行为。

您可以获得任何价值,崩溃或任何收获。

由于“完全符合我的期望”是“未定义行为”的一部分,因此您不能像“它符合我的预期那样进行推理,因此它不是未定义但可以正常运行”。 那条推理是无效的。

释放并不意味着系统将修改在那里存储的值(这将涉及不必要的开销)。 这仅表示此存储空间可用于将来的存储。

因为您正在调用未定义的行为。 当您做自己的事情时,任何事情都会发生。

再详细一点:局部范围内定义的变量(不是“静态”)位于一个区域中,C规范仅模糊地将其称为“ 自动内存 ”。 一旦离开函数的范围,就不再定义该函数调用的自动存储器中的所有内容。 明确说明:自动内存与函数无关,它与函数的特定调用相关。

实现内存自动分配/取消分配的实际方法是实现定义的。 但是,有一种事实上的标准方法可以实现自动存储,称为堆栈帧

堆栈是指定的内存区域,其中每个函数调用都会将现有堆栈顶部该内存区域的另一个块分配给该函数调用的范围。 因此,对于程序中函数调用链中的每个函数调用,另一个大块的自动存储区域被放在先前分配的区域的顶部,即,它形成了自动存储区域的堆栈。 当剩下一个函数作用域时,并且仅在函数调用链中的最后一个函数发生时,关联堆栈帧的下落就被简单地丢弃了。 但是,在通常的实现中不会破坏存储在那里的内容。 这意味着,只要您不知道要查找的位置,只要没有调用其他函数,您都将发现留下的垃圾。

因此,在基于堆栈框架的实现中,不会对作用域退出进行清理,将变量的地址保存在自动内存中并将其传递给更高的作用域就可以了,并且可能会为您提供不再有效的作用域的内容。 但是实现可以有效地选择将堆栈存储器的未使用部分标记为对OS无效,并且尝试访问它也可能使程序崩溃。

函数返回后将释放a (因为它仅在函数的主体作用域中可见),因此您的ptr指针指向未分配的内存地址。 由于释放不会删除该地址上的值(只是将地址标记为空闲),因此在访问该地址上的ptr时仍可能显示值12。

函数中局部变量的地址取决于调用函数时执行时堆栈的状态(SP寄存器的值)。

因此,每次调用函数时,局部变量可能具有不同的地址,一旦您不在函数外部,就不能依赖该地址的内容

我知道您的问题已经很好地回答了,但是如果您有兴趣了解幕后的情况,请联系我们。 只是为了让您对这些内容的原因和工作原理有了更清晰的了解,请尝试以下操作:

#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 再次,我说机会是因为不需要这样的实现来对堆栈进行布局。

其要点是,当您在堆栈上分配内容时,它会一直保留在那里,直到有新内容出现在堆栈上为止(另一个堆栈框架)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM