删除[]是否等于删除?

时间:2009-10-12 08:27:45

标签: c++ memory-management pod

IP_ADAPTER_INFO *ptr=new IP_ADAPTER_INFO[100];

如果我免费使用

delete ptr;

是否会导致内存泄漏,如果不是,为什么呢?

这是VS2005生成的反汇编代码

; delete ptr;
0041351D  mov         eax,dword ptr [ptr] 
00413520  mov         dword ptr [ebp-0ECh],eax 
00413526  mov         ecx,dword ptr [ebp-0ECh] 
0041352C  push        ecx  
0041352D  call        operator delete (4111DBh) 
00413532  add         esp,4 

; delete []ptr;
00413535  mov         eax,dword ptr [ptr] 
00413538  mov         dword ptr [ebp-0E0h],eax 
0041353E  mov         ecx,dword ptr [ebp-0E0h] 
00413544  push        ecx  
00413545  call        operator delete[] (4111E5h) 
0041354A  add         esp,4 

6 个答案:

答案 0 :(得分:146)

这是否会导致内存泄漏,擦拭你的硬盘,让你怀孕,让讨厌的鼻腔恶魔在你的公寓周围追逐你,或者让一切正常,没有明显的问题,是不确定的。它可能是这种方式与一个编译器,并改变另一个,改变与新的编译器版本,每个新的编译,与月相,你的心情,或取决于在最后一个阳光下通过处理器的中微子的数量下午。或者它可能不会。

所有这一切,以及无限多种其他可能性都放在一个术语中:未定义的行为

远离它。

答案 1 :(得分:13)

仅举例说明某些操作系统和编译器上的某些“未定义”行为。希望人们可以调试他们的代码。

测试1

#include <iostream>
using namespace std;
int main()
{
  int *p = new int[5];
  cout << "pass" << endl;
  delete p;
  return 0;
}

测试2

#include <iostream>
using namespace std;
int main()
{
  int *p = new int;
  cout << "pass" << endl;
  delete[] p;
  return 0;
}

测试3

#include <iostream>
using namespace std;
struct C {
  C() { cout << "construct" << endl; }
  ~C() { cout << "destroy" << endl; }
};

int main()
{
  C *p = new C[5];
  cout << "pass" << endl;
  delete p;
  return 0;
}

测试4

#include <iostream>
using namespace std;
struct C {
  C() { cout << "construct" << endl; }
  ~C() { cout << "destroy" << endl; }
};

int main()
{
  C *p = new C;
  cout << "pass" << endl;
  delete[] p;
  return 0;
}
  • Windows 7 x86,msvc 2010.使用默认选项进行编译,即启用异常处理程序。

测试1

pass

测试2

pass

测试3

construct
construct
construct
construct
construct
pass
destroy
# Then, pop up crash msg

测试4

construct
pass
destroy
destroy
destroy
destroy
destroy
destroy
destroy
... # It never stop until CTRL+C
  • Mac OS X 10.8.5,llvm-gcc 4.2或gcc-4.8生成相同的输出

测试1

pass

测试2

pass

测试3

construct
construct
construct
construct
construct
pass
destroy
a.out(71111) malloc: *** error for object 0x7f99c94000e8: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
zsh: abort      ./a.out

测试4

construct
pass
a.out(71035) malloc: *** error for object 0x7f83c14000d8: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
zsh: abort      ./a.out
  • Ubuntu 12.04,AMD64,gcc 4.7

测试1

pass

测试2

pass

测试3

construct
construct
construct
construct
construct
*** glibc detected *** ./a.out: munmap_chunk(): invalid pointer: 0x0000000001f10018 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7fe81d878b96]
./a.out[0x400a5b]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7fe81d81b76d]
./a.out[0x4008d9]
======= Memory map: ========
....
zsh: abort (core dumped)  ./a.out

测试4

construct
destroy
destroy
destroy
destroy
destroy
destroy
destroy
destroy
...
destroy
destroy
*** glibc detected *** ./a.out: free(): invalid pointer: 0x00000000016f6008 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7fa9001fab96]
./a.out[0x400a18]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7fa90019d76d]
./a.out[0x4008d9]
======= Memory map: ========
...
zsh: abort (core dumped)  ./a.out

答案 2 :(得分:7)

它通常不会泄漏,因为如果POD析构函数很简单,并且不需要调用它们,那么delete只需释放数组所占用的内存。内存释放只需要一个指针值,因此它将返回到堆中。该数组接受一个连续的内存块,因此释放可以成功,就像它是一个单元素的释放一样。

但不要依赖于此,因为它是未定义的行为。也许它可以正常工作,也许发生了可怕的事情,在这个编译器上工作,在另一个上工作并且很多人都感谢你种错误。

有关详细信息,请参阅this answer

答案 3 :(得分:4)

删除:     仅为指向的元素调用适当的析构函数(如果需要),     然后释放内存块

删除[] :     为其数组中的每个元素调用适当的析构函数(如果需要),     然后释放内存块

答案 4 :(得分:3)

如果A指向通过新T [n]分配的数组,则必须通过删除[] A将其删除。

<强>为什么吗

删除和删除[]之间的区别很简单 - 前者会破坏标量对象而后者会破坏数组。

更多信息herehere

更新1(错误):

如果在使用新T [n]分配时使用delete(而不是delete []),则只释放第一个元素而其他元素不被析构化,这将导致内存泄漏。为什么?这就是Bjarne Stroustrup和其他人设计这种语言的方式。它不是编译器变化的。如果一个编译器以不同的方式解除分配,那么它只是不遵循标准The C++ Programming Language,第6.2.6.2和19.4.5章。

更新2(正确):

我承认我在使用新T [n]进行分配时使用delete运算符的行为的错误。阅读上面提到的文档我没有找到确切的描述,所以我认为在这种情况下行为将是 undefined ,并且从编译器到编译器会有所不同。例如,AFAIK,MSVC编译器将生成与GCC不同的代码。 请忽略更新1

答案 5 :(得分:-4)

对于 POD 的数组,不泄漏(使用大多数编译器)。例如, MSVC 删除生成相同的代码,为 POD 生成删除[] 强>

就个人而言,我认为C / C ++可能没有operator delete []。编译器知道对象大小,并且已分配的内存大小在运行时是已知的,因此知道指针数组是否非常简单,并以正确的方式处理内存。

编辑:

好的,伙计们。你可以测试你的编译器,并说它是否泄漏?

尝试将其视为编译器开发人员。我们有新[] 删除删除[] 。每个都有自己的删除。似乎完美而完整。当你打电话给删除[] 时,让我们看看发生了什么?

1. call vector destructor for an object
2. actual free memory

什么是 POD 的析构函数?没有! 因此,为 POD 数组调用删除不会泄露!即使它打破了标准。即使不推荐。

EDIT2:

这是VS2008生成的反汇编代码:

operator delete[]:
78583BC3  mov         edi,edi 
78583BC5  push        ebp  
78583BC6  mov         ebp,esp 
78583BC8  pop         ebp  
78583BC9  jmp         operator delete (78583BA3h)