delete / delete[] Bin mir etwas unsicher



  • Hallo zusammen,

    ich hab mich heute schon etwas durch euer Forum gelesen und einige interesante Threads gelesen (Irgendwie auch zu Problemen die mich erst beim lesen interesiert haben.)

    Ich habe auch kurz ein paar Fragen zu delete / delete []. Oder besser gesagt bräuchte ich nur eine Bestätigung, dass ich mich nicht irre.

    Also folgendes kleines Prg.

    #include <stdio>
    #include <string>
    
    int main(void)
    {
    
    	char** pArray(0);
    	char* pString1(0);
    	char* pString2(0);
    
    	pArray = new char* [5];
    	memset(pArray,0x2A,5*sizeof(char*));
    
    	pString1 = new char[5];
    	memset(pString1,0x2B,5);
    
    	pString2 = new char[5];
    	memset(pString2,0x2B,5);
    
    	sprintf(pString1,"Hallo");
    	sprintf(pString2,"HHHHH");
    
    	pArray[0] = pString1;
    	pArray[1] = pString2;
    
    	pString1 = 0;
    	pString2 = 0;
    
    	delete [] pArray[0];
    	delete [] pArray[1];
    
    	delete [] pArray;
    
    	printf("Ending \n");
    }
    

    1. Speicherleck Ja / Nein ?
    Eigentlich geht es mir hier um die delete[] Aufrufe! Passt das so? Oder muss ich fuer jedes Array-Feld den delete [] Befehl ausführen?( Also eine for-Schleife um jedes delete []?)

    2. Wenn der delete / delete [] Befehl ausgeführt wird, wird dann der ehemals allokierte Speicherbereich irgendwie reinitialisiert ( Also irgendwie mit 0x0 beschrieben oder so?? )
    Diese Frage ziehlt auf folgendes ab. Wenn ich mir unter Eclipse/CDT die ehemals allokierten Speicherbereiche von pArray/pString1/String2 anschaue, dann werden jeweils immer die ersten 4 Byte auf 0x0 gesetzt!

    3. Muss der Pointer der an delete / delete [] übergeben wird, der gleiche Typ sein wie der Typ den ich beim allokieren des Speichers verwende??

    Zu 3. Geht sowas?

    char* pArray(0);
    int* pInt(0);
    .
    pArray = new char[5];
    pInt = reinterpret_cast(int*,pArray);
    delete [] pInt;
    .
    .
    

    Das was ich unter 2. und 3. geschrieben habe setzt natürlich voraus, dass meine Annahme unter 1. stimmt??

    Schonmal vielen Dank im voraus!!

    Mfg

    ziro



  • Valgrind main zu deinem obigen Code folgendes:

    ==9950== Memcheck, a memory error detector.
    ==9950== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
    ==9950== Using LibVEX rev 1804, a library for dynamic binary translation.
    ==9950== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
    ==9950== Using valgrind-3.3.0-Debian, a dynamic binary instrumentation framework.
    ==9950== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
    ==9950== For more details, rerun with: -v
    ==9950== 
    ==9950== Invalid write of size 1
    ==9950==    at 0x4026BD7: memcpy (mc_replace_strmem.c:402)
    ==9950==    by 0x8048602: main (in /home/christoph/dev/test/delete)
    ==9950==  Address 0x42b0075 is 0 bytes after a block of size 5 alloc'd
    ==9950==    at 0x4024F14: operator new[](unsigned) (vg_replace_malloc.c:268)
    ==9950==    by 0x804859F: main (in /home/christoph/dev/test/delete)
    ==9950== 
    ==9950== Invalid write of size 1
    ==9950==    at 0x4026BD7: memcpy (mc_replace_strmem.c:402)
    ==9950==    by 0x804861D: main (in /home/christoph/dev/test/delete)
    ==9950==  Address 0x42b00ad is 0 bytes after a block of size 5 alloc'd
    ==9950==    at 0x4024F14: operator new[](unsigned) (vg_replace_malloc.c:268)
    ==9950==    by 0x80485C9: main (in /home/christoph/dev/test/delete)
    ==9950== 
    ==9950== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 17 from 1)
    ==9950== malloc/free: in use at exit: 0 bytes in 0 blocks.
    ==9950== malloc/free: 3 allocs, 3 frees, 30 bytes allocated.
    ==9950== For counts of detected errors, rerun with: -v
    ==9950== All heap blocks were freed -- no leaks are possible.
    

    => Keine Speicherleks aber nicht korrekt wenn ich das richtig interpretiere.

    btw: meines Wissens gibt es die <cstdio> und die <stdio.h> aber keine <stdio>

    3. Sollte IMHO nicht korrekt sein


  • Mod

    1. Das ist ok, bis auf das sprintf, das zu undefiniertem Verhalten führt (schreibt ein Zeichen zuviel)
    Die anderen Zeiger in pArray sind ja singulär, ein delete darf auf keinen Fall mit diesen durchgeführt werden.
    2. unspezifiziert - da jeder Zeiger auf den zuvor allokierten Speicher singulär wird, ist es gar nicht möglich, dies zu beobachten ohne undefiniertes Verhalten zu produzieren. Der Compiler darf den Speicher also reinitialiseren (z.B. als Debughilfe) muss aber nicht. Das gleiche gilt übrigens für das Argument von delete selbst, wenn es sich um eine Variable handelt (obwohl das über die Objektrepräsentation durchaus beobachtbar ist).
    3. Für delete muss das Argument entweder exakt (in Typ und Wert) mit dem Ergebnis des entsprechenden newsübereinstimmen, oder ein Zeiger auf eine Basisklasse mit virtuellem Destruktor sein (sofern an der allozierten Stelle ein vollständiges Objekt vom ursprünglich allokierten Typ lebt).
    Bei delete [] muss Typ und Wert in jedem Falle übereinstimmen (Arrays sind nicht polymorph).



  • Weiß nicht ganz was deine std::memset da bewirken sollen, aber ansonsten:

    1. Speicherleck Ja / Nein ?
    Eigentlich geht es mir hier um die delete[] Aufrufe! Passt das so? Oder muss ich fuer jedes Array-Feld den delete [] Befehl ausführen?( Also eine for-Schleife um jedes delete []?)

    delete [] pArray[0];
    delete [] pArray[1];
    
    delete [] pArray;
    

    pArray[2], pArray[3] und pArray[4] zeigen doch noch auf keinen, von dir gewählten, Speicherplatz. Worauf die im Moment zeigen ist schlicht undefiniert. Speicher den du nicht anforderst, musst du auch nicht freigeben. Von d.h. reichen die beiden delete [] pArray[...]. Dann das delete [] pArray als Gegenstück zu pArray = new char* [5]; und ja, alles ist sauber 😉

    2. Wenn der delete / delete [] Befehl ausgeführt wird, wird dann der ehemals allokierte Speicherbereich irgendwie reinitialisiert ( Also irgendwie mit 0x0 beschrieben oder so?? )
    Diese Frage ziehlt auf folgendes ab. Wenn ich mir unter Eclipse/CDT die ehemals allokierten Speicherbereiche von pArray/pString1/String2 anschaue, dann werden jeweils immer die ersten 4 Byte auf 0x0 gesetzt!

    Ehm nein, es ist nicht definiert wo die hinzeigen. D.h. gehen viele hin und schreiben sich einfach folgendes:

    template <typename pointer_type>
    inline void safe_delete(pointer_type*& pointer)
    { delete pointer; pointer = NULL; }
    

    . Dann kannst du durch einfachen vergleich auf NULL herausfinden, ob der Zeiger noch auf etwas zeigt und es kann dir dann auch nichts passieren, falls du versehentlich(oder nicht 😉 ) delete pointer aufrufst, denn delete 0 ist als "nichts machen" definiert.

    3. Muss der Pointer der an delete / delete [] übergeben wird, der gleiche Typ sein wie der Typ den ich beim allokieren des Speichers verwende?

    Ja.

    char* ptr_char(new char[5]());
    int* ptr_int(reinterpret_cast<int*>(ptr_char));
    /* use ptr_int */
    
    /* BUT still free ptr_char! */
    delete [] ptr_char;
    

    und wenn du die C-Header übernehmen willst, heißt es <cstdio> und <cstring>. <string> beinhaltet die Klasse std::string und <stdio> gibt es schlicht und ergreifend nicht.



  • Schau dir doch auch mal die std::string-Klasse an, die ist richtig komfortabel. Mit strings in C-Manier herumzufrimeln gehört sich in C++ nicht, das ist nämlich fehleranfällig3, unübersichtlich und, vor allem, nervig 😉



  • Guten morgen,

    vielen Dank erstmal fuer all die Antworten!!!

    1. Das kleine Prog. ist eine stark gestripte Version eines Prog.

    2. Die memsets dienen eigentlich nur der Initialiserung. Die Werte sind frei gewaehlt und mit absicht 0x0 verschieden damit ich wenn ich mir den Speicherbereich ansehe sofort erkenne was nun geaendert wurde und was nicht.

    3. Natuerlich gibt es in c++ schoenere Konstrukte als Arrays aber die kann ich leider nicht so einfach verwenden, da mein Programm mit einem Shared Memory umgehen muss, den ich definitiv nicht anpassen kann. ( Da wuerden mir ca. 200 Menschen den Kopf abreisen!! 😃 Und ich hab nur einen!!)

    Nochmal zurueck zu Frage 3.
    Was meint ihr den zu sowas:

    .
    char* pArray(0);
    char* pStart(0);
    .
    pArray = new char[5];
    .
    .
    pStart = pArray;
    .
    .
    delete [] pStart;
    .
    .
    

    Der Typ ist ja nun der gleiche nur halt nicht DER Pointer der von new zurueckgegeben wurde.

    Mfg

    ziro



  • ziro-base schrieb:

    ...
    Nochmal zurueck zu Frage 3.
    Was meint ihr den zu sowas:
    ...
    Der Typ ist ja nun der gleiche nur halt nicht DER Pointer der von new zurueckgegeben wurde.

    Der Zeiger mag ein anderer sein, nicht aber sein Inhalt. Wenn du einen Zeiger einen anderen Zuweist, zeigen diese auf den gleichen Speicherbereich, es wird hierbei nichts kopiert (daher ist es vollkommen egal auf welchen der beiden Zeiger man ein delete macht, gelöscht wird dennoch der Inhalt dahinter).

    Ich markiere mal ein paar Stellen und erläutere sie anschließend

    char* pArray(0);
    char* pStart(0);      // A
    .
    pArray = new char[5]; // B
    .
    pStart = pArray;      // C
    .
    delete [] pStart;     // D
    

    Anfangs (A):

    pArray ----> Zeigt auf 0
    pStart ----> Zeigt auf 0

    Anlegen (B):

    pArray ----> Zeigt auf den neuen Bereich sagen wir dieser Beginnt bei 0x3456
    pStart ----> Zeigt auf 0

    Zuweisen (C):

    pArray ----> Zeigt auf 0x3456
    pStart ----> Zeigt auf 0x3456 (Der Zielbereich wird nicht kopiert!)

    Zuweisen (D):

    pArray ----> Zeigt auf 0x3456 => Nun gelöschter Bereich
    pStart ----> Zeigt auf 0x3456 => Nun gelöschter Bereich

    cu André


Anmelden zum Antworten