Ist löschen[] gleich löschen?

Lesezeit: 10 Minuten

Ist loschen gleich loschen
Satbir

IP_ADAPTER_INFO *ptr=new IP_ADAPTER_INFO[100];

wenn ich kostenlos benutze

delete ptr;

Wird es zu einem Speicherleck führen, wenn nicht, warum?

Dies ist ein von VS2005 generierter Disassemblierungscode

; 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 

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

    – Satbir

    12. Oktober 2009 um 8:40 Uhr

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

    – Andrejs Cainikovs

    12. Oktober 2009 um 8:45 Uhr

  • @Andrej: Nein, das ist nicht sicher. Es könnte so passieren, aber vielleicht auch nicht. Bei PODs ist es sogar wahrscheinlich, dass dies nicht der Fall ist. Aber du weißt nie.

    – sbi

    12. Oktober 2009 um 8:49 Uhr

  • Was wird wann passieren IP_ADAPTER_INFO hört auf, POD-Typ zu sein? Werden Sie den gesamten Code bearbeiten? Sie haben Ihre Frage mit dem C++-Tag getaggt, also sollten Sie die Verwendung in Betracht ziehen std::vector.

    – Kirill W. Ljadwinski

    12. Oktober 2009 um 8:52 Uhr

  • Ich empfehle dringend, diese Frage zu ignorieren und stattdessen zu lesen [delete vs delete[]](stackoverflow.com/questions/4255598/delete-vs-delete), deren Antworten viel treffender sind.

    – Theodor Murdock

    11. Mai 2016 um 20:31 Uhr

Ist loschen gleich loschen
sbi

Ob dies zu einem Speicherleck führt, Ihre Festplatte löscht, Sie schwanger macht, fiese Nasal Demons dazu bringt, Sie durch Ihre Wohnung zu jagen, oder alles ohne offensichtliche Probleme gut funktionieren lässt, ist undefiniert. Das kann bei einem Compiler so sein und bei einem anderen, bei einer neuen Compiler-Version, bei jeder neuen Compilation, bei den Mondphasen, Ihrer Stimmung oder je nach Anzahl der Neutrinos, die am letzten Sonnigen durch den Prozessor gelaufen sind Nachmittag. Oder vielleicht auch nicht.

All das und unendlich viele andere Möglichkeiten werden in einem Begriff zusammengefasst: Undefiniertes Verhalten:

Halte dich einfach davon fern.

  • Ich würde sagen, dass “undefiniertes Verhalten” “schlecht” ist und vermieden werden sollte, wie Sie sagen. Aber das bedeutet auch, dass es in einigen Fällen tatsächlich zu einem Speicherleck kommt und Sie immer so codieren sollten, dass Sie Worst-Case-Szenarien lösen. Das ist jedenfalls meine Meinung.

    – Philipp Ekberg

    12. Oktober 2009 um 8:46 Uhr

  • @Filip: Angenommen, Ihr eigenes Programm ruft undefiniertes Verhalten auf? Ist das eine weiterentwickelte Form der defensiven Programmierung?

    – Michael Foukarakis

    12. Oktober 2009 um 9:30 Uhr

  • +1, um richtig zu sein. Apropos wie delete und delete[] verhält sich in einer bestimmten Implementierung einfach falsch. Es ist ein undefiniertes Verhalten: Tu es nicht.

    – jalf

    12. Oktober 2009 um 10:03 Uhr

1647172818 504 Ist loschen gleich loschen
jichi

Nur eine Veranschaulichung einiger “undefinierter” Verhaltensweisen auf bestimmten Betriebssystemen und Compilern. Ich hoffe, es könnte für die Leute hilfreich sein, ihren Code zu debuggen.

Prüfung 1

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

Prüfung 2

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

Prüfung 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;
}

Prüfung 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. Mit Standardoptionen kompilieren, dh Ausnahmehandler ist aktiviert.

Prüfung 1

pass

Prüfung 2

pass

Prüfung 3

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

Prüfung 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 erzeugen dieselbe Ausgabe

Prüfung 1

pass

Prüfung 2

pass

Prüfung 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

Prüfung 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

Prüfung 1

pass

Prüfung 2

pass

Prüfung 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

Prüfung 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

  • Ihr Test zeigt immer noch nicht, ob es Speicherlecks geben würde.

    – SOFe

    30. November 2017 um 6:17 Uhr

1647172819 46 Ist loschen gleich loschen
scharfer Zahn

Es wird normalerweise nicht lecken, da im Falle von POD-Destruktoren trivial sind und es nicht erforderlich ist, sie so aufzurufen delete gibt nur den vom Array belegten Speicher frei. Die Speicherfreigabe erfordert nur einen Zeigerwert, damit er an den Heap zurückgegeben wird. Das Array nimmt einen zusammenhängenden Speicherblock auf, sodass die Freigabe erfolgreich sein kann, als wäre es die Freigabe eines einzelnen Elements.

Verlassen Sie sich jedoch nicht darauf, da es sich um ein undefiniertes Verhalten handelt. Vielleicht funktioniert es gut, vielleicht passiert etwas Schreckliches, funktioniert auf diesem Compiler, funktioniert nicht auf einem anderen und viele Leute danken Ihnen für das Einpflanzen eines Fehlers.

Siehe diese Antwort für Details.

  • Weil new T[] kann ein hinzufügen sizeof offset unabhängig von der POD-ness von T, in diesem Fall delete[] wird dies kompensieren. delete würde den Header-Zuordnungsblock um einige Bytes verfehlen, eine (möglicherweise nicht initialisierte) Elementanzahl als Header interpretieren und eine unvorhersehbare Heap-Beschädigung verursachen. Was sich natürlich nur zeigt, wenn der Chef hinschaut.

    – MSalter

    12. Oktober 2009 um 9:18 Uhr

  • Sicher, deshalb das Wort meistens Gibt es. Und Haufenkorruption ist kein Leck.

    – scharfer Zahn

    12. Oktober 2009 um 9:23 Uhr

  • struct A { void operator delete[](void *p, size_t t) { } }; obwohl struct A ist ein POD, new A[10] wird diese Größe zuweisen und speichern, und delete[] muss es abrufen und an den Operator delete übergeben

    – Johannes Schaub – litb

    12. Oktober 2009 um 10:34 Uhr

löschen : ruft die entsprechenden Destruktor nur für das Element zeigt (falls erforderlich) und gibt dann den Speicherblock frei

löschen[] : ruft die entsprechenden Destruktoren für jedes Element in seinem Array (falls erforderlich) und gibt dann den Speicherblock frei

1647172820 306 Ist loschen gleich loschen
Andrejs Cainikovs

Verwenden des Löschoperators für Zuweisungen mit neuem T[n] ist nicht definiert und variiert von Compiler zu Compiler. AFAIK, der MSVC-Compiler zum Beispiel erzeugt einen anderen Code als GCC.

Wenn A auf ein Array zeigt, das über new T allokiert wurde[n]dann müssen Sie es über Löschen löschen[] A. Der Unterschied zwischen löschen und löschen[] ist unkompliziert – Ersteres zerstört ein Skalarobjekt und Letzteres zerstört ein Array.

  • Niemand (nicht einmal Bjarne, geschweige denn irgendein Björn) hat angegeben, dass nur ein Objekt befreit werden würde. Es ist nicht definiert. (Und wie ich bereits an anderer Stelle sagte, habe ich mit mindestens einem Compiler gearbeitet, der sie alle befreit hat.)

    – sbi

    12. Oktober 2009 um 8:57 Uhr

  • dieser Compiler – Ich sehe keinen Verweis auf einen bestimmten Compiler (mehr) ? „Undefiniertes Verhalten“ ist ein ISO-Standardbegriff und bezieht sich nicht auf eine bestimmte Implementierung.

    – MSalter

    12. Oktober 2009 um 9:14 Uhr

  • Der C++-Standard sagt ausdrücklich (zumindest in der mir vorliegenden Entwurfsversion vom 13.9.2001), dass ein solches Verhalten undefiniert ist: Bei der ersten Alternative (delete object) soll der Wert des Operanden von delete ein Zeiger auf ein non sein -Array-Objekt oder ein Zeiger auf ein Unterobjekt (1.8), das eine Basisklasse eines solchen Objekts darstellt (Klausel 10). Wenn nicht, ist das Verhalten undefiniert. Bei der zweiten Alternative (Array löschen) soll der Wert des Operanden von delete der Zeigerwert sein, der aus einem vorherigen Array new-expression resultierte.72) Wenn nicht, ist das Verhalten undefiniert.

    – Komat

    12. Oktober 2009 um 9:32 Uhr

  • Keines davon existiert in meiner Kopie (C++98). Wie auch immer, der Standard gibt nur an, was Sie vor den Updates gesagt haben: Einer zerstört ein Skalarobjekt, der andere zerstört ein Array. Es tut nicht Sagen Sie, was Sie in Ihrem ersten Update haben, dass, wenn der skalare Löschvorgang für ein Array aufgerufen wird, das erste Objekt zerstört wird. Das ist nicht durch die Norm garantiert. Es ist einfach undefiniert.

    – jalf

    12. Oktober 2009 um 10:06 Uhr

  • Danke jalf, sbi, Komat. Du hattest Recht, ich hatte meinen Fehler zugegeben und den Beitrag aktualisiert.

    – Andrejs Cainikovs

    12. Oktober 2009 um 10:43 Uhr

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

Persönlich denke ich, dass C/C++ ohne Operator Delete auskommen könnte[]. Der Compiler kennt die Objektgröße und die Größe des zugewiesenen Speichers ist zur Laufzeit bekannt, daher ist es sehr einfach zu wissen, ob es sich um ein Zeigerarray handelt oder nicht, und den Speicher richtig zu verteilen.

BEARBEITEN:

Okay Leute. Können Sie Ihren Compiler testen und sagen, ob er leckt?

Versuchen Sie, als Compiler-Entwickler zu denken. Wir haben Neu, Neu[], löschen, löschen[]. Jeder Neu hat sein eigenes löschen. Scheint perfekt und vollständig zu sein. Mal sehen, was los ist, wenn Sie anrufen löschen[]?

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

Wozu dient der Destruktor POD? Gar nichts! Also anrufen löschen für Array von POD wird nicht auslaufen! Auch wenn es den Standard bricht. Auch wenn es nicht empfehlenswert ist.

EDIT2:

Dies ist ein von VS2008 generierter Disassemblierungscode:

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

  • Niemand (nicht einmal Bjarne, geschweige denn irgendein Björn) hat angegeben, dass nur ein Objekt befreit werden würde. Es ist nicht definiert. (Und wie ich bereits an anderer Stelle sagte, habe ich mit mindestens einem Compiler gearbeitet, der sie alle befreit hat.)

    – sbi

    12. Oktober 2009 um 8:57 Uhr

  • dieser Compiler – Ich sehe keinen Verweis auf einen bestimmten Compiler (mehr) ? „Undefiniertes Verhalten“ ist ein ISO-Standardbegriff und bezieht sich nicht auf eine bestimmte Implementierung.

    – MSalter

    12. Oktober 2009 um 9:14 Uhr

  • Der C++-Standard sagt ausdrücklich (zumindest in der mir vorliegenden Entwurfsversion vom 13.9.2001), dass ein solches Verhalten undefiniert ist: Bei der ersten Alternative (delete object) soll der Wert des Operanden von delete ein Zeiger auf ein non sein -Array-Objekt oder ein Zeiger auf ein Unterobjekt (1.8), das eine Basisklasse eines solchen Objekts darstellt (Klausel 10). Wenn nicht, ist das Verhalten undefiniert. Bei der zweiten Alternative (Array löschen) soll der Wert des Operanden von delete der Zeigerwert sein, der aus einem vorherigen Array new-expression resultierte.72) Wenn nicht, ist das Verhalten undefiniert.

    – Komat

    12. Oktober 2009 um 9:32 Uhr

  • Keines davon existiert in meiner Kopie (C++98). Wie auch immer, der Standard gibt nur an, was Sie vor den Updates gesagt haben: Einer zerstört ein Skalarobjekt, der andere zerstört ein Array. Es tut nicht Sagen Sie, was Sie in Ihrem ersten Update haben, dass, wenn der skalare Löschvorgang für ein Array aufgerufen wird, das erste Objekt zerstört wird. Das ist nicht durch die Norm garantiert. Es ist einfach undefiniert.

    – jalf

    12. Oktober 2009 um 10:06 Uhr

  • Danke jalf, sbi, Komat. Du hattest Recht, ich hatte meinen Fehler zugegeben und den Beitrag aktualisiert.

    – Andrejs Cainikovs

    12. Oktober 2009 um 10:43 Uhr

997490cookie-checkIst löschen[] gleich löschen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy