2009-10-12 9 views
41
IP_ADAPTER_INFO *ptr=new IP_ADAPTER_INFO[100]; 

, wenn ich mit dem kostenlosenIst delete [] gleich zu löschen?

delete ptr; 

wird es zu Speicherverlust führen, wenn nicht, dann warum? Dies erzeugt Demontage Code von 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 
+2

Was sagen die Dokumente? – spender

+0

Ich habe gelesen, dass der Destruktor für das erste Element im Array aufgerufen wird, aber der gesamte Speicher freigegeben wird, kann ich beim Debuggen sehen – Satbir

+0

Nein, nur das erste Element wird freigegeben, andere nicht. –

Antwort

146

Ob dies zu einem Speicherverlust führt, wischt Ihre Festplatte, macht Sie schwanger, macht böse Nasal Demons jagen Sie rund um Ihre Wohnung, oder lässt alles gut ohne offensichtliche Probleme funktioniert, ist undefined. Es könnte so sein mit einem Compiler, und ändern mit einem anderen, ändern Sie mit einer neuen Compiler-Version, mit jeder neuen Kompilation, mit den Mondphasen, Ihrer Stimmung oder abhängig von der Anzahl der Neutrinos, die durch den Prozessor am letzten sonnigen passiert Nachmittag. Oder vielleicht nicht.

, dass alle, und eine unendliche Anzahl von anderen Möglichkeiten setzt in einen Begriff: undefiniertes Verhalten:

Gerade von ihrem fernhalten.

+0

Ich würde sagen, dass "Undefined Behaviour" ist "schlecht" und sollte vermieden werden, wie Sie sagen. Aber dies bedeutet auch, dass es in einigen Fällen tatsächlich Speicher auslaufen und Sie sollten immer Code, so dass Sie Worst-Case-Szenarien lösen. Das ist meiner Meinung nach sowieso. –

+5

@Filip: Angenommen, Ihr eigenes Programm ruft undefined Verhalten? Ist das eine Form der defensiven Programmierung entwickelt? –

+20

+1 für ein korrektes. Sprechen über wie 'löschen' und 'delete []' verhält sich in einer bestimmten Implementierung, gibt nur die falsche Idee. Es ist undefiniertes Verhalten: Tu es nicht. – jalf

3

Wenn ein auf ein Array, das über neue T [n] zugewiesen wurde, dann Sie sie löschen müssen über delete [] A.

Warum?

Der Unterschied zwischen delete und delete [] ist einfach - ersterer zerstört ein skalares Objekt und letzteres zerstört ein Array.

Weitere Informationen here und here.

UPDATE 1 (falsch):

Wenn Sie löschen verwenden (und nicht gelöscht werden []), während mit neuen T [n] Zuweisung nur erste Element freigegeben wird, während andere nicht destructorized, die führen wird zu Speicherleck. Warum? So haben Bjarne Stroustrup und andere die Sprache entworfen. Und es ist nicht Compiler-Variation. Wenn ein Compiler den anderen Weg freigibt, ist es nur , der nicht dem Standard folgt. The C++ Programming Language, Kapitel 6.2.6.2 und 19.4.5.

UPDATE 2 (richtig):

ich mein Fehler über das Verhalten des Einlassens mit löschen Operator auf Zuweisungen mit neuen T [n]. Beim Lesen des erwähnten Dokuments habe ich die genaue Beschreibung nicht gefunden, also nehme ich an, dass das Verhalten in dieser Situation undefined ist und von Compiler zu Compiler variieren wird. AFAIK, MSVC Compiler zum Beispiel wird anderen Code als GCC produzieren. Bitte ignorieren UPDATE 1.

+1

Niemand (nicht einmal Bjarne, geschweige denn ein Björn) gab an, dass nur ein Objekt freigegeben werden würde. Es ist _undefined_. (Und wie ich an anderer Stelle gesagt habe, habe ich mit mindestens einem Compiler gearbeitet, der sie alle befreit hat.) – sbi

+0

Ich nehme an, du meinst Bjarne, nicht Björn? – gnud

+0

Korrigiert. Sorry, Bjarne :-) –

7

Es wird in der Regel nicht undicht, weil im Falle von POD Destruktoren trivial sind und es keine Notwendigkeit gibt, sie aufzurufen, so delete nur deallocates Speicher belegt durch das Array. Speicherdeallokation erfordert nur einen Zeigerwert, so dass es zum Heap zurückgegeben wird. Das Array verwendet einen zusammenhängenden Speicherblock, und so kann die Aufhebung der Zuordnung erfolgreich sein, als ob es sich um eine Freigabe eines einzelnen Elements handeln würde.

Aber verlassen Sie sich nicht darauf, da es undefiniertes Verhalten ist. Vielleicht klappt es gut, vielleicht passiert etwas Schreckliches, arbeitet an diesem Compiler, arbeitet nicht an einem anderen und viele Leute danken dir, dass du einen Fehler gemacht hast.

Weitere Informationen finden Sie unter this answer.

+0

Warum war das? s downvoted Ich frage mich ... – sharptooth

+2

Weil 'new T []' einen 'sizeof' Offset unabhängig von der POD-ness von T hinzufügen kann, in diesem Fall wird 'delete []' dies kompensieren. 'delete' würde den Header-Zuweisungsblock um einige Bytes verfehlen, einen (möglicherweise nicht initialisierten) Elementzähler als Kopfzeile interpretieren und eine unvorhersehbare Heap-Beschädigung verursachen. Was natürlich erst dann auftaucht, wenn der Chef schaut. – MSalters

+2

Sicher, deshalb ist das Wort * normalerweise * da. Und Heap-Korruption ist kein Leck. – sharptooth

-4

Für Array von POD es wird nicht auslaufen (mit den meisten Compiler). Zum Beispiel MSVC erzeugt identisch Code für löschen und delete [] für Array von POD.

Ich persönlich denke, C/C++ könnte ohne Operator löschen [] sein. Der Compiler kennt die Objektgröße und die zugewiesene Speichergröße ist zur Laufzeit bekannt, daher ist es sehr einfach zu wissen, ob es sich um ein Zeigerarray handelt oder nicht, und den Speicher auf eine richtige Weise zu verteilen.

EDIT:

OK, Leute. Können Sie an Ihrem Compiler testen und sagen, ob es undicht ist?

Versuchen Sie, als Compiler-Entwickler zu denken. Wir haben neue, new [], löschen, löschen []. Jede neue hat ihre eigene löschen. Scheint perfekt und vollständig. Mal sehen, was passiert, wenn Sie löschen [] anrufen?

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

Was ist destructor für POD? Nichts! So rufen löschen für Array von POD wird nicht leak! Selbst wenn es den Standard bricht. Auch wenn es nicht empfohlen wird.

EDIT2:

Dies ist die Demontage Code von VS2008 generiert:

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

'delete []' ist definitiv keine Form der Optimierung. Es geht um Korrektheit. – sbi

+0

Ja, aber ist überflüssig. Die Leute wollen wissen, ob es undicht wird und ich habe geantwortet. Warum runterstimmen? –

+0

Übrigens mag ich KORREKTHEIT. Aber wir diskutieren es nicht ... –

2

löschen: die entsprechende destructor für das Element nur Anrufe zu spitz (falls erforderlich), dann gibt frei der Speicherabschnitt

löschen [] : ruft die entsprechenden Destruktoren für jedes Element in seiner Array (falls erforderlich), dann gibt den Speicher chunk

+2

All dies ist wahr ** nur **, wenn 'delete' und' delete [] 'korrekt verwendet werden. Diese Frage bezieht sich speziell auf den Fall, in dem 'delete' verwendet wird, wenn' delete [] 'verwendet werden sollte. –

13

Nur eine Darstellung einiger „undefiniert“ Verhalten bei bestimmten OSes und Compiler. Ich hoffe, es könnte für Leute hilfreich sein, ihren Code zu debuggen.

-Test 1

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

Test-2

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

Test-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; 
} 

Test-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. Kompilieren mit Standardoptionen, d. H. Ausnahmeroutine ist aktiviert.

-Test 1

pass 

Test-2

pass 

Test-3

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

Test-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 oder gcc-4.8 die gleiche Ausgabe

-Test 1

pass 

-Test 2

pass 

Test 3 erzeugen

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 

Test-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

-Test 1

pass 

Test-2

pass 

Test 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 

-Test 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 
+0

Ihr Test zeigt immer noch nicht, ob Speicherlecks vorhanden sind. – SOFe