删除[]是否等于删除?

Sat*_*bir 44 c++ memory-management pod

IP_ADAPTER_INFO *ptr=new IP_ADAPTER_INFO[100];
Run Code Online (Sandbox Code Playgroud)

如果我免费使用

delete ptr;
Run Code Online (Sandbox Code Playgroud)

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

这是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 
Run Code Online (Sandbox Code Playgroud)

sbi*_*sbi 149

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

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

只是远离它.

  • +1是正确的.谈论`delete`和`delete []`在某些特定实现中的表现如何只是给出了错误的想法.它是未定义的行为:不要这样做. (23认同)
  • @Filip:假设你自己的程序调用未定义的行为?是一些进化形式的防御性编程? (7认同)

jic*_*chi 14

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

测试1

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

测试2

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

测试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;
}
Run Code Online (Sandbox Code Playgroud)

测试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;
}
Run Code Online (Sandbox Code Playgroud)
  • Windows 7 x86,msvc 2010.使用默认选项进行编译,即启用异常处理程序.

测试1

pass
Run Code Online (Sandbox Code Playgroud)

测试2

pass
Run Code Online (Sandbox Code Playgroud)

测试3

construct
construct
construct
construct
construct
pass
destroy
# Then, pop up crash msg
Run Code Online (Sandbox Code Playgroud)

测试4

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

测试1

pass
Run Code Online (Sandbox Code Playgroud)

测试2

pass
Run Code Online (Sandbox Code Playgroud)

测试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
Run Code Online (Sandbox Code Playgroud)

测试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
Run Code Online (Sandbox Code Playgroud)
  • Ubuntu 12.04,AMD64,gcc 4.7

测试1

pass
Run Code Online (Sandbox Code Playgroud)

测试2

pass
Run Code Online (Sandbox Code Playgroud)

测试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
Run Code Online (Sandbox Code Playgroud)

测试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
Run Code Online (Sandbox Code Playgroud)


sha*_*oth 7

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

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

有关详情,请参阅此答案.

  • `struct A {void operator delete [](void*p,size_t t){}};`即使`struct A`是`POD`,`new A [10]`将分配并存储该大小,` delete []`必须检索它并将其传递给operator delete (3认同)
  • 因为`new T []`可能会添加一个`sizeof`偏移,而不管T的POD是什么,在这种情况下`delete []`会补偿这个.`delete`将错过标题分配块几个字节,将(可能未初始化的)元素计数解释为标头,并导致不可预测的堆损坏.当然,只有当老板看起来时才会出现这种情况. (2认同)
  • 当然,这就是**通常*这个词的原因.堆损坏不是泄漏. (2认同)

And*_*ovs 5

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

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

  • C++ 标准明确指出(至少在我手头的 13.9.2001 草案版本中)这种行为是未定义的:在第一个选择(删除对象)中,delete 的操作数的值应该是一个指向非-array 对象或指向表示此类对象的基类的子对象 (1.8) 的指针(第 10 条)。如果不是,则行为未定义。在第二种选择(删除数组)中,删除操作数的值应是由先前数组新表达式产生的指针值。72) 如果不是,则行为未定义。 (4认同)
  • 我的副本(C++98)中都不存在这些。无论如何,该标准仅指定您在更新之前所说的内容:一个销毁标量对象,另一个销毁一个数组。它*没有*说明您在第一次更新中的内容,如果在数组上调用标量删除,则第一个对象将被销毁。这是标准的**不**保证。它只是未定义。 (3认同)

小智 5

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

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

  • 仅当正确使用`delete`和`delete []`时,所有这些才是真的。这个问题专门针对在应该使用`delete []`的情况下使用`delete`的情况。 (3认同)