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

これがメモリリークにつながるか、ハードディスクを消去するか、妊娠させるか、厄介な鼻の悪魔があなたのアパートの周りを追いかけるか、明らかな問題なしにすべてが正常に機能するかどうかは未定義です. あるコンパイラーではこのようになり、別のコンパイラーでは変更される可能性があり、新しいコンパイラーのバージョンで変更されるか、新しいコンパイルごとに変更されるか、月の満ち欠け、気分、または最後の晴れた日にプロセッサを通過したニュートリノの数に応じて変更される可能性があります午後。またはそうではないかもしれません。

これらすべてと、無数の他の可能性が 1 つの用語にまとめられます:未定義の動作:

近づかないでください。

于 2009-10-12T08:41:47.137 に答える
14

特定の OS およびコンパイラでの「未定義」の動作の例です。人々がコードをデバッグするのに役立つことを願っています。

テスト 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

deletePOD デストラクタの場合は自明であり、それらを呼び出す必要がないため、配列によって占有されているメモリの割り当てを解除するだけなので、通常はリークしません。メモリーの解放にはポインター値のみが必要なため、ヒープに戻されます。配列はメモリの連続したブロックを受け入れるため、単一の要素の割り当て解除であるかのように、割り当て解除が成功する可能性があります。

ただし、未定義の動作であるため、これに依存しないでください。問題なく動作するかもしれませんし、何か恐ろしいことが起こるかもしれません。このコンパイラでは動作し、別のコンパイラでは動作しません。多くの人がエラーを植え付けてくれてありがとう。

詳細については、この回答を参照してください。

于 2009-10-12T08:55:31.530 に答える
5

new T[n] を使用した割り当てで削除演算子を使用することは未定義であり、コンパイラによって異なります。私の知る限り、たとえば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[]があります。それぞれのnewには独自のdeleteがあります。完璧で完全なようです。delete[]を呼び出したときに何が起こっているか見てみましょう。

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

PODのデストラクタとは? 何もない!したがって、PODの配列に対してdeleteを呼び出してもリークしません! たとえそれが基準を破ったとしても。推奨されていなくても。

EDIT2:

これは、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 に答える