【问题标题】:Valgrind does not report memory leak on "delete array"Valgrind 不报告“删除数组”的内存泄漏
【发布时间】:2012-05-22 02:23:15
【问题描述】:

在实现下面的 C++ 代码后,我运行了valgrind --leak-check=full 以检查是否有任何内存泄漏。结果是退出时使用了 0 个字节,并且不可能有泄漏

但是,后来我发现我忘记在析构函数中使用delete[] x 而不是只使用delete x

我搜索了一些解释(例如:delete vs delete[] operators in C++),我读到的所有内容都说使用delete 而不使用[] 会导致内存泄漏,因为它只调用数组中第一个对象的析构函数。

我将代码更改为 delete[] 并且 valgrind 输出是相同的(如预期的那样)。但是现在我很困惑:“valgrind 有问题吗,或者delete 即使没有运算符[] 也确实适用于数组?”

#include <iostream>
#include <string.h>
using namespace std;
class Foo {
  private: char *x;
  public:
    Foo(const char* px) {
       this->x = new char[strlen(px)+1];
       strcpy(this->x, px);
    }
    ~Foo() {
       delete x;
    }
    void printInfo() {  cout << "x: " << x << endl;  }
};
int main() {
   Foo *objfoo = new Foo("ABC123");
   objfoo->printInfo();
   delete objfoo;
   return 0;
}

【问题讨论】:

  • 您链接的问题的答案未提及内存泄漏。事实上,他们说的和下面 Als 说的一样。那你为什么说“我读到的所有内容都说使用不带[]的删除会导致内存泄漏。”
  • 另外,在您的程序标志上使用 valgrind 3.2.1 时会出现“不匹配的 free() / delete / delete[]”。这不是内存泄漏,而是错误。
  • 众所周知,例如,当数组包含像char 这样的内置类型时,MSVC 恰好为delete xdelete[] x 生成相同的代码。对于带有析构函数的类类型,代码不同
  • 虽然您使用不同的工具检测它,但此处的代码和问题可能与 _CrtMemDumpAllObjectsSince() function is not able to detect leaks if delete array called instead of delete []array 重复,我对这个问题的回答在这里也同样适用。
  • 谢谢杰瑞,我找不到这个问题。那里的答案很有趣

标签: c++ memory-leaks valgrind delete-operator


【解决方案1】:

使用不带 [] 的 delete 会导致内存泄漏。

不,它会导致未定义行为

您使用new [] 分配和使用delete 取消分配的程序具有未定义的行为。其实你很幸运(相当不幸)它没有表现出一些奇怪的行为。

附带说明,您还需要为您的班级关注 Rule of Three。目前,您不这样做,并且在不久的将来会找麻烦。

【讨论】:

  • 不能说得更好了,但我会引用常见问题解答,以便提问者可以参考正在发生的事情:“获取新 T 之间的联系是程序员而不是编译器的责任[n] 和 delete[] p 正确。如果你弄错了,编译器将不会生成编译时和运行时错误消息。可能会导致堆损坏。或者更糟。你的程序可能会死掉。” parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.12
  • Stephen,是的,我知道这是程序员的责任,我也知道后果。我只是想了解为什么 valgrind 找不到可能的泄漏。
  • Als,也感谢旁注。未定义的行为是否意味着没有人知道会发生什么?但是众所周知,堆损坏很可能(正如斯蒂芬所引用的那样),那么为什么 valgrind 找不到呢?
  • @MoacirPontiJr.:是的未定义行为意味着任何事情(字面意思)都是可能的。一旦程序具有未定义的行为,您就不能指望任何行为,所有的赌注都关闭了。另外,正如我在回答 new []delete 不匹配的结果时所说的那样,未定义的行为可能是内存泄漏,但不一定是。跨度>
【解决方案2】:

deletedelete[] 之间的主要区别不在于内存释放,而在于析构函数调用。

虽然形式上是未定义的行为,但实际上它或多或少会起作用...除了delete 只会调用第一项的析构函数。

附带说明,您可能会遇到问题:

#include <iostream>

struct A{
  ~A() { std::cout << "destructor\n"; }
};

int main() {
  A* a = new A[10];
  delete a;
}

有点像:

*** glibc detected *** ./prog: munmap_chunk(): invalid pointer: 0x093fe00c ***
======= Backtrace: =========
/lib/libc.so.6[0xb759dfd4]
/lib/libc.so.6[0xb759ef89]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/libstdc++.so.6(_ZdlPv+0x21)[0xb77602d1]
./prog(__gxx_personality_v0+0x18f)[0x8048763]
./prog(__gxx_personality_v0+0x4d)[0x8048621]
======= Memory map: ========

查看ideone

【讨论】:

  • 我认为这是 OP 的争论,因为他的 delete 调用在析构函数中。
  • 当“删除新 T[n]”时,大多数类型的大多数实现都会(幸运地)失败,因为它们将大小存储在分配的前面,因此对于底层 raw 的调用具有略微偏移的指针内存管理器
猜你喜欢
  • 2020-09-17
  • 1970-01-01
  • 1970-01-01
  • 2015-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-25
  • 1970-01-01
相关资源
最近更新 更多