Woher weiß delete[], wann schluss ist?



  • Hallo @all,

    Betrachten wir folgenden Code:

    int main(int argc, char *argv[])
    {
    	int* i = new int[20];
    	delete[] i;
    	return NULL;
    }
    

    Arbeite ich mit Zeiger-Arrays, sieht so eigentlich mein Standardverfahren zum allozieren bzw. deallozieren des Speichers aus.
    Die Sache, die ich nicht verstehe ist, woher weiß der delete[]-operator, wann schluss ist? D.h. woher weiß delete, lösche 20 ints? Das wirkt auf mich ein bisschen wie schwarze Magie, da die Größe nirgends "zusehen" ist.

    Das zweite Problem ist:

    int* i = malloc( sizeof(int) * 20);
    i = realloc( i , sizeof(int) * 40);
    free(i);
    

    in C hab ich die Möglichkeit, mein Zeiger-Array zu erweitern / zuverkleinern.
    Dafür habe ich die Funktion realloc(...); Gibt es so eine Funktion auch für new und delete? Ich weiß, dass es vector gibt, aber angenommen, man würde gerne die alten Arrays verwenden, gäbe es dann eine Möglichkeit?

    mfG

    Hlymur


  • Mod

    new/delete (und auch malloc/realloc/free) merken sich irgendwo, wie viel Speicher (und wie viele Objekte) zu einem Zeiger gehören. Wie genau sie das machen, bleibt der Implementierung überlassen. Möglich wäre zum Beispiel ein statischer Speicher für die Verwaltungsdaten oder eine Speicherung der Verwaltungsdaten in dem dynamisch erzeugten Bereich selbst (z.B. könnte malloc einfach etwas mehr Speicher besorgen als gefordert. In den ersten paar Bytes speichert es, wie viel Speicher dort ist. An den Nutzer gibt es dann einen Zeiger hinter diese Verwaltungsdaten zurück).

    Es gibt kein realloc-Äquivalent zu new/delete.



  • Die Bibliothek speichert wie viel Bytes hinter einem Pointer hängen - oder organisiert sonst irgendetwas, um den Speicher wieder frei zu geben. (Oder eben auch nicht, solche Systeme soll's geben. ;)) Was genau da passiert, wird nicht spezifiziert. Eine Methode ist einfach mehr zu allokieren und in die ersten 4/8 byte zu schreiben wie groß der Bereich ist. (Du bekommst dann einen Pointer hinter diese Größe.)

    Nein, es gibt kein "C++-Äquivalent" zu realloc. Du hast da btw einen Speicherleck drin, realloc gibt den Speicher von i nicht frei, das muss also heißen:

    int* p = malloc(sizeof(int) * 20);
    int* q = realloc(p, sizeof(int) * 40);
    p = q ? q : p;
    free(p);
    

    Und dass du std::vector kennst ist gut, denn

    Arbeite ich mit Zeiger-Arrays, sieht so eigentlich mein Standardverfahren zum allozieren bzw. deallozieren des Speichers aus.

    das sollte nie der Fall sein.



  • Zu deiner ersten Frage:
    Dem Compiler sind ja sämtliche Möglichkeiten offengelassen, dass er z.b. vor der Zeigerposition noch einen kleinen Bereich mit allokiert, in die er z.B. die Größe des Arrays reinschreibt.
    Beim deallokieren könnte er dann da schauen und dementsprechen löschen.

    Auf der anderen Seite stellt sich erst mal die Frage, wie denn Speicher vergeben wird und wie eben diese Vergabe nachgehalten wird.
    Es kann ja eine Tabelle angelegt werden, in der jeweils die Startadresse eienr Größe zugeordnet wird. Abhängig davon kann der Allokator entscheiden wo/wie/was noch genug Platz, um neuen Speicher zu allokieren. D.h. das Speicherlöschen wäre das Entfernen eines Eintrags aus der Tabelle und die Größe würde niemals gebraucht werden.

    Gibt viele Arten, die nach Rom führen.

    Zu deiner zweiten Frage:
    Nein, sowas wie "renew" gibts in C++ nicht.Dafür gibts std::vector, std::array und alle anderen Datenstrukturen.
    Ja Möglichkeiten gibt es, du schreibst dir die Funktion selbst. oder du nutzt die C Funktionen.

    Aber genau das ist ja der Vorteil von C++ gegenüber C. Du musst dich, wenn du es nicht willst, nicht um alles selbst kümmern.

    Zitat: ...Blabla C scheisst dir schnell in den Fuss, blablabla C++ schiesst dirs ganze Bein weg blabla...



  • Danke euch, für die schnelle Hilfe 🙂



  • return NULL; in der main-Funktion ist sowohl in C als auch in C++ falsch. main liefert ein int, keinen Pointer!


  • Mod

    manni66 schrieb:

    return NULL; in der main-Funktion ist sowohl in C als auch in C++ falsch. main liefert ein int, keinen Pointer!

    NULL ist in C++ ein Makro für 0. Edit²: Das muss ich wohl noch weiter eingrenzen: In C++98. In C++11 ist dafür überraschenderweise auch nullptr erlaubt.
    Falls du aber meintest, es würde etwas falsches andeuten, dann hast du Recht.



  • manni66 schrieb:

    return NULL; in der main-Funktion ist sowohl in C als auch in C++ falsch. main liefert ein int, keinen Pointer!

    http://www.stroustrup.com/bs_faq2.html#null

    In C++, the definition of NULL is 0

    Nein, ich habe weder im Header noch im Standard (C++98/03, pre11) nachgesehen und weiss nicht, ob dieses FAQ noch aktuell in Bezug auf C++98/03/pre11 ist.



  • Arcoth schrieb:

    Falls du aber meintest, es würde etwas falsches andeuten, dann hast du Recht.

    In C++98 deutet es falsch an btw. zeigt, dass man nicht verstanden hat, wa man gerade tut,, in C ist es schlichtweg falsch. In einem Votrag sagte STL (wenn ich mich richtig erinnere), dass er versucht habe, NULL als nullptr zu definieren, das dann aber gelassen habe, da an zu vielen Stellen 0 (nicht als Pointer) gemeint war.



  • GCC nach ist es keine so gute Idee

    #include <cstddef>
    
    int main()
    {
    	return NULL;
    }
    
    main.cpp: In function 'int main()':
    main.cpp:5:9: warning: converting to non-pointer type 'int' from NULL [-Wconversion-null]
      return NULL;
             ^
    

  • Mod

    Ja, weil es seit C++11 eben auch nullptr sein darf, und der Code dann nicht mehr portabel ist.


  • Mod

    NULL ist schon immer (sowohl in C als auch C++) eine durch die Implementation definierte Nullpointerkonstante.

    C und C++03 und C++11 haben allerdings unterschiedliche Definitionen, was eine Nullpointerkonstante ist.

    In allen Sprachen ist ein integraler konstanter Ausdruck mit dem Wert 0 eine Nullpointerkonstante.
    (Nur) In C ist ein solcher, nach void* gecasteter Ausdruck ebenfalls eine Nullpointerkonstante.
    In C++11 ist ein prvalue des Typs std::nullptr_t ebenfalls eine Nullpointerkonstante.


Anmelden zum Antworten