【问题标题】:Accessing deleted memory访问已删除的内存
【发布时间】:2016-12-01 02:54:37
【问题描述】:

在测试我对堆栈的实现(即使用链表)时,我发现了一件有趣的事情。有一个测试代码可以重现它:

#include <iostream>
using namespace std;

struct Node {
    int val;
    Node *prev;
};

int main() {
    Node *first = new Node;
    first->val = 20;
    first->prev = NULL;

    cout << "first:" << first << endl;

    Node *p = new Node;
    p->val = 40;
    p->prev = first;

    delete p;

    cout << "p->val:" << p->val << endl;
    cout << "p->prev:" << p->prev << endl;
}

输出:

first:0x22cfc20
p->val:0
p->prev:0x22cfc20


但是如果我像这样交换struct Node 定义中的元素顺序:

struct Node {
    Node *prev;
    int val;
};

输出将是

first:0x195dc20
p->val:40
p->prev:0


当然,在这两种情况下它都是未定义的行为,但也许它存在一些合理的解释,为什么它会以这种方式工作?还是只是随机的?我尝试多次运行代码,但每次都给出相同的输出(特定地址值除外)并且从未崩溃。

【问题讨论】:

  • 未定义的行为并不真正有趣
  • “未定义行为”表示“未定义行为”。您观察到的实际行为在很大程度上取决于您的实现的堆管理例程。由于您甚至没有指定您的操作系统,因此无法在此处收集更多信息。
  • 一旦您更改编译器选项(如优化),您的程序可能会开始表现不同。然后你会发现寻找未定义行为的原因是徒劳的。在某些时候,您会意识到这是在浪费时间,而且您还有更好的事情要做(例如开发应用程序)。

标签: c++ pointers memory-management undefined-behavior


【解决方案1】:

正如其他人所说,对于这种情况,我们几乎无法明确说明,这不仅是因为未定义的行为,还因为我们对编译它的平台一无所知。

但是有几点可以说:

更改结构中成员的顺序可能会导致编译器在成员之间或结构末尾应用不同的填充,以满足当前编译时使用的任何对齐规则。

如果在这种情况下确实发生了这种情况,那么它可能会影响在堆中分配的内存布局,从而影响当堆空间被释放然后重用时哪些数据会被覆盖。这可能解释了为什么结果不同。

【讨论】:

    【解决方案2】:

    未定义的行为根本不指定任何特定的行为。一个实现可以被允许让你的程序工作,或者关闭你的计算机,格式化你的硬盘,在你的猫体内打开一个黑洞,甚至在你的黑洞里打开一只猫。

    如果程序试图对内存进行无效操作,大多数实现将停止执行。有时,如果您不走运,您的程序会像您的示例一样正常运行。

    话虽如此:您可能能够访问已删除的内存有数百个原因,但任何关于这样做的行为的假设都是无效的,并且可能因任何原因导致不同的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-08
      • 1970-01-01
      • 1970-01-01
      • 2012-06-15
      • 1970-01-01
      • 1970-01-01
      • 2022-10-24
      • 2019-08-17
      相关资源
      最近更新 更多