为什么这个C ++代码有效?堆栈内存和指针

时间:2013-12-25 21:48:27

标签: c++ pointers memory stack

这是一些C ++代码。

#include <iostream>
using namespace std;

class test{
    int a;
  public:
    test(int b){
    a = b;
    cout << "test constructed with data " << b << endl;
  }
  void print(){
    cout << "printing test: " << a << endl;
  }
};

test * foo(){
    test x(5);
    return &x;
}

int main()
{
    test* y = foo();
    y->print();
    return 0;
}

这是它的输出:

test constructed with data 5
printing test: 5

我的问题:为什么指向x的指针仍然在函数foo的上下文之外“工作”?据我所知,函数foo创建了一个test实例并返回该对象的地址。

函数退出后,变量x超出范围。我知道C ++不是垃圾收集 - 变量超出范围时会发生什么?为什么在foo()中返回的地址仍指向看似有效的对象?

如果我在某个范围内创建一个对象,并希望在另一个范围内使用它,我应该在堆中分配它并返回指针吗?如果是这样,我何时/何地将其删除

由于

2 个答案:

答案 0 :(得分:8)

x是一个局部变量。 foo返回后,无法保证x所在堆栈中的内存损坏或完好无损。这是未定义行为的本质。在阅读x之前运行一个函数,你会发现引用“死”变量的危险:

void nonsense(void)
{
    int arr[1000] = {0};
}

int main()
{
    test* y = foo();
    nonsense();
    y->print();
    return 0;
}

我机器上的输出:

  

用数据5构建的测试   印刷测试:0

答案 1 :(得分:3)

当变量超出范围时,将调用析构函数(对于非POD数据),并且该变量占用的位置现在被视为未分配,但实际上并未写入内存,因此旧值仍然存在。这并不意味着您仍然可以安全地访问此值,因为它位于标记为“空闲”的位置。新变量可以驻留,也可以在此内存空间中进行分配。
内存未被擦除的原因是因为你实际上无法擦除内存,你可以做的就是像所有零或全部或随机一样写一些内容,这不仅没有意义,而且性能下降。

它与垃圾收集无关。垃圾收集器不会“擦除”内存,但会将其标记为空闲。您所描述的行为存在于C而不是Java中的原因不是垃圾收集器,而是C允许您通过指针访问任何您想要,分配或不分配,有效或无效的内存,而Java不会t(公平地说,垃圾收集器是Java可以制作的原因,因此您无法访问任何内存)。

可以对删除文件时磁盘上发生的事情进行类比。文件内容保留(它们不会被覆盖),而是修改文件系统中的指针(句柄),以便磁盘上的内存被认为是空闲的。这就是为什么特殊工具可以恢复已删除的文件:信息仍然存在,直到有新的内容写入它,如果你可以指向它,你就可以获得它。与C中的指针几乎相同。想想每次删除4GB文件时在磁盘上实际写入4GB是什么意思。没有必要在内存中为超出范围的每个变量写入该变量的整个大小。你只需将其标记为免费。