【问题标题】:Why I have to call delete on the "original" pointer?为什么我必须在“原始”指针上调用 delete?
【发布时间】:2012-01-10 15:53:49
【问题描述】:

我想知道为什么在下面的代码中第一次删除不会释放内存:

#include <list>
#include <stdio.h>

struct abc {
    long a;

    abc() {
        puts("const");
    }
    ~abc() {
        puts("desc");
    }
};

int main() {

    std::list<abc*> test;

    abc* pA = new abc;
    printf("pA: 0x%lX\n", (unsigned long int)pA);
    test.push_back(pA);

    abc* pB = test.back();

    printf("pB: 0x%lX\n", (unsigned long int)pB);
    delete pB; // just ~abc()

    test.pop_back();

    delete pA; // ~abc() and free (works)

    puts("before double-free");

    delete pA; // ~abc() and second free (crash)

    return 0;
}

输出是:

const
pA: 0x93D8008
pB: 0x93D8008
desc
desc
before double-free
desc
*** glibc detected *** ./test: double free or corruption (fasttop): 0x093d8008 ***
...

我也尝试了free(),但行为相同。

【问题讨论】:

  • 您要删除同一个对象 3 次?对于每个 new,您应该有 一个 删除。
  • “为什么在下面的代码中第一次删除不会释放内存” - 确实如此。
  • 内存被释放得很好......第一次时间。
  • @ronag:作为规则的一个例外:除非您将新的东西存储在智能指针中。
  • @phresnel:确实,删除是由智能指针完成的。因此,更好的定义是“除非其他人删除”。

标签: c++ pointers memory delete-operator


【解决方案1】:
delete pA; // ~abc() and free (works)

puts("before double-free");

delete pA; // ~abc() and second free (crash)

这些delete 语句在您编写delete pB不需要。你有一个误解,认为delete pB 只调用析构函数。不,它调用析构函数并释放内存。

另外,由于您已经编写了delete pB,接下来的两个delete 表达式调用undefined behavior,这意味着任何事情都可能发生:程序可能可能不会 崩溃!

看看这些主题:

【讨论】:

  • 我知道,但是如果有双重释放,程序应该会崩溃,但不会。
  • @quarry:你认为它为什么会崩溃?这是未定义的行为。
  • @quarry:不,如果你试图释放已经释放的东西,你会调用未定义的行为。程序可能会崩溃,或者打印一条消息,或者它可能会导致恶魔从你的鼻子里飞出来。
  • @quarry:再次查看我的答案。我也回答了那部分。
  • @Nawaz 我已经读过了。每次我做类似双释放程序崩溃但我知道它不是 100%。现在我有其他意见了。谢谢,这让我的问题更清楚了。
【解决方案2】:

您的第一次删除使指针指向无效状态。因此,现在使用该指针会导致未定义的行为。

int* p = new int;
delete p;
delete p; //undefined behaviour

按照标准,这保证没问题(正如第一条评论中指出的那样):

int* p = new int;
delete p;
p = 0;
delete p; //fine

【讨论】:

  • 标准保证你的第二个 sn-p 没问题。
  • @SteveJessop 感谢您指出这一点。我已经编辑了答案。
【解决方案3】:

首先,你不能用new分配free内存,你必须使用delete

其次,仅仅因为glibc没有立即检测到双重释放,你怎么知道delete pB;没有释放它?这就是delete 所做的事情,并且从您自己的日志记录中,您每次都将相同的地址传递给delete

第三:你真正想要完成什么?

【讨论】:

    【解决方案4】:

    glibc 抱怨之前,您必须在pA 上调用delete 两次,这只是您的编译器/平台的一个怪癖……例如,在我当前的平台上,即OSX 10.6.8 和@987654324 @ 版本 i686-apple-darwin10-gcc-4.2.1,我得到以下输出:

    const
    pA: 0x100100080
    pB: 0x100100080
    desc
    desc
    test(14410) malloc: *** error for object 0x100100080: pointer being freed was not allocated
    *** set a breakpoint in malloc_error_break to debug
    Abort trap
    

    因此,您可以在 pA 上看到对 delete 的第一次调用,根据标准,这会导致未定义的行为,因为您没有将指针设置为 NULL0,确实被抓为尝试在我的平台上由 C++ 运行时释放已释放的内存。

    由于双重删除的结果是“未定义的行为”,这实际上取决于实现和平台发生的情况。

    如果您通过使用-lmcheck 标志编译,使用针对 glibc 内存一致性检查器的链接,您可能能够更快地检测到分配/解除分配内存错误。

    【讨论】:

      猜你喜欢
      • 2011-04-18
      • 2012-10-30
      • 1970-01-01
      • 1970-01-01
      • 2018-06-27
      • 2011-10-04
      • 2016-06-02
      • 2010-10-12
      • 1970-01-01
      相关资源
      最近更新 更多