47
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 
4

6 回答 6

154

这是否会导致内存泄漏、擦除你的硬盘、让你怀孕、让讨厌的鼻恶魔在你的公寓里追逐你,或者让一切正常工作而没有明显的问题,都是不确定的。一个编译器可能会这样,另一个编译器会改变下午。或者它可能不会。

所有这一切,以及无数其他可能性都归为一个术语:未定义的行为

远离它。

于 2009-10-12T08:41:47.137 回答
14

只是某些操作系统和编译器上的一些“未定义”行为的说明。希望它可以帮助人们调试他们的代码。

测试 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
于 2013-09-24T04:54:36.310 回答
7

它通常不会泄漏,因为在 POD 析构函数的情况下是微不足道的,并且不需要调用它们,因此delete只需释放数组占用的内存即可。内存释放只需要一个指针值,因此它将返回到堆中。该数组占用一个连续的内存块,因此释放可以成功,就像它是单个元素的释放一样。

但是不要依赖它,因为它是未定义的行为。也许它工作正常,也许发生了可怕的事情,在这个编译器上工作,在另一个编译器上不起作用,许多人感谢你植入错误。

有关详细信息,请参阅此答案

于 2009-10-12T08:55:31.530 回答
5

在带有 new T[n] 的分配上使用 delete 运算符是未定义的,并且会因编译器而异。AFAIK,例如 MSVC 编译器将产生与 GCC 不同的代码。

如果 A 指向一个通过 new T[n] 分配的数组,那么你必须通过 delete[] A 删除它。 delete 和 delete[] 之间的区别很简单——前者破坏一个标量对象,后者破坏一个数组.

于 2009-10-12T08:38:10.373 回答
5

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

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

于 2011-08-11T14:22:32.247 回答
-4

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

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

编辑:

好,朋友们。你能在你的编译器上测试并说它是否泄漏吗?

尝试以编译器开发人员的身份思考。我们有newnew[]deletedelete[]。每个的都有自己的删除。看起来完美而完整。让我们看看调用delete[]时发生了什么?

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

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

编辑2:

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

operator delete[]:
78583BC3  mov         edi,edi 
78583BC5  push        ebp  
78583BC6  mov         ebp,esp 
78583BC8  pop         ebp  
78583BC9  jmp         operator delete (78583BA3h) 
于 2009-10-12T09:09:47.283 回答