【问题标题】:deleting c++ array from heap and memory leak从堆中删除 C++ 数组和内存泄漏
【发布时间】:2018-04-17 02:27:28
【问题描述】:

我有一个关于从堆内存中删除数组的问题。在一本书和this blog 和其他资源such as this one 中,我读到要从堆中删除一个数组,我们必须在delete 关键字之后使用[],这样如果我们不使用[],我们会有内存泄漏。

例如,考虑下面的代码。

//constructing array
int *s = new int[10];


// deleting array from heap
delete [] s;

我在 Linux 中测试了这个小程序,使用 valgrind 包来检查有多少内存泄漏是由错误的编码引起的。在Linux中通过以下命令,我看到一切都很好

sudo valgrind --leak-check=full ./<path_to_exe_file>

这是 Linux 命令的输出

 ==4565== HEAP SUMMARY:
 ==4565==     in use at exit: 0 bytes in 0 blocks
 ==4565==   total heap usage: 1 allocs, 1 frees, 40 bytes allocated
 ==4565== 
 ==4565== All heap blocks were freed -- no leaks are possible

但是,当我尝试使用 delete 而不使用 [] 时,我的问题出现了。 valgrind 的输出显示所有堆内存已被释放。它是否正确?或valgrind 没有意识到堆没有被释放并且数组的某些部分仍然在那里!!?如果valgrind无法检测到这种内存泄漏,有没有其他包可以检测到这种情况?

【问题讨论】:

  • int *s = new int[10]; delete s; 给出未定义的行为。运算符delete的形式需要与运算符new的形式相匹配。未定义行为的一种可能表现形式是按照您的预期运行,例如您所看到的。使用另一个编译器,它可能会以不同的方式运行,或者根本不运行。
  • delete 你是什么new-ed 和delete[] 你是什么new[]-ed。或者甚至更好地使用智能指针。
  • int 替换为 std::vector&lt;int&gt;std::string 以查看您的 UB 程序中的预期泄漏。
  • 表达式中的空格对编译器没有影响,只对人类有影响。
  • 如果我使用[],使用std::vector&lt;int&gt; 很奇怪,部分内存已经丢失。 @Jarod42

标签: c++ arrays memory-leaks valgrind


【解决方案1】:

在不使用[] 的情况下对数组调用delete 会导致未定义的行为。未定义的行为可能是数组被正确删除,这似乎是您观察到的。但是,您不能依赖它。

【讨论】:

  • 感谢您的帖子和其他 cmets,我的问题的答案是内存泄漏,但 valgrind 无法检测到 :)
  • 不,没有内存泄漏——你很幸运。
  • 哇,我不知道这可能会发生
  • @saeedmasoomi 未定义的行为通常会导致所需的行为。例如,如果您使用int* get_invalid_pointer(){int a = 3; return &amp;a;} 并将其称为int* b = get_invalid_pointer(); cout &lt;&lt; *b &lt;&lt; endl;,则实际上很可能会打印“3”。如果您在获取int* b =... 和打印之间放置一些代码,它很有可能不再起作用。这对你来说是未定义的行为。最糟糕的情况是代码似乎可以正常工作。
  • 最糟糕的是当它seems 在测试时工作。然后在生产中意外失败。
【解决方案2】:

Martin Broadhurst 已经给出了正确的language-lawyer answer。我将给出技术细节的答案:

使用delete[] 而不是delete 的要点是,delete 运算符无法知道传递的指针是指向数组还是指向单个对象。因此,delete 只删除一个对象,而delete[] 调用一些额外的魔法来恢复数组的大小,然后继续删除所有元素。

现在删除包含两个不同的部分:

  1. 必须通过调用析构函数来销毁对象。对于数组,这意味着对每个数组元素调用一次析构函数。

  2. 必须将已使用的内存标记为空闲,以便可以重复使用。这是 C++ 中全局 operator delete() 的工作。由于数组是连续存储的,因此这是对整个数组的一次调用。

valgrind 只关心内存。因此,它会挂钩内存分配函数,如malloc()free()operator new()operator delete()

当您调用delete 而不是delete[] 时会发生什么,第一个对象被破坏,并且指针被传递给operator delete()operator delete() 不知道存储在内存区域内的对象,无论如何它们已经被销毁,因此它会成功地将内存区域标记为空闲。 valgrind 看到这个 operator delete() 调用,并且很高兴,因为所有内存都可以重用。但是,您的代码未能正确破坏除第一个数组元素之外的所有元素。这很糟糕。

【讨论】:

  • 前几天我写了一个程序。当我运行这个程序时,完成任务需要 2 分钟!但是当我使用valgrind 包时,输出仅在 3 秒内就准备好了,我猜valgrind 只是对代码进行了粗略的了解!谢谢你的回答,对我很有帮助。
  • @saeedmasoomi 如果任何程序在valgrind 下的执行速度比没有时更快,那么该程序代码有很大问题:valgrind 基本上使用模拟 CPU 执行程序,在每次内存访问时添加边界检查,并且这些边界检查需要时间。在valgrind 下执行时,预计任何进程至少会慢 50 倍。相反的意思是,你的程序在valgrind 下运行时会做一些非常不同的事情,这意味着它表现出未定义的行为。如果我是你,我肯定会调查此事。
  • 谢谢,我检查了一下,它们的运行时间都是一样的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-21
  • 2012-05-22
  • 2011-08-20
相关资源
最近更新 更多