Realloc auf NULL-wertigen (oder undefinierten) Zeiger

Lesezeit: 7 Minuten

Ich habe darüber gelesen realloc und war verwirrt über einen dort erwähnten Punkt. Betrachten Sie den folgenden Code:

#include <stdio.h>
#include <stdlib.h>

int main () {

    int* ptr = NULL;
    ptr = realloc(ptr, 10*sizeof(int));
    return 0;
}

Besteht eine Gefahr bei der Speicherzuweisung mit realloc mit dem anfangs NULL-geschätzt ptr? Wenn statt:

int* ptr = NULL;

Ich hatte das:

int* ptr; // no value given to ptr

Wäre es ein Problem anzurufen realloc verwenden ptr?

  • ptr = realloc(ptr, 10*sizeof(int)); Wenn realloc fehlschlägt, verlieren Sie Speicher.

    – Ayxan Haqverdili

    10. Januar 2020 um 4:28 Uhr

  • @AyxanHaqverdili, aber in diesem Fall kann kein Speicher verloren gehen.

    – Kaihaku

    20. September 2021 um 6:07 Uhr

  • @Kaihaku Ich habe wahrscheinlich über den allgemeinen Fall gesprochen.

    – Ayxan Haqverdili

    20. September 2021 um 6:13 Uhr

Besteht eine Gefahr bei der Zuweisung von Speicher mit realloc unter Verwendung des anfänglich NULL-wertigen ptr

Keiner

7.22.3.5

Wenn ptr ein Nullzeiger ist, verhält sich die Funktion realloc für die angegebene Größe wie die Funktion malloc.

Zum zweiten Teil:

int* ptr; // no value given to ptr

Wäre es ein Problem, realloc mit ptr aufzurufen?

Wenn Sie nicht initialisierte Zeiger verwenden, ist dies in der Tat ein sehr ernstes Problem, da Sie nicht vorhersagen können, welchen Wert sie haben werden. Die Funktion realloc funktioniert nur korrekt für NULL oder Werte erhalten aus malloc / realloc.

Andernfalls, wenn ptr nicht mit einem Zeiger übereinstimmt, der zuvor von einer Speicherverwaltungsfunktion zurückgegeben wurde […] das Verhalten ist undefiniert

  • Beachten Sie, dass der Grund dafür darin besteht, dass die meisten Implementierungen von malloc Speichern Sie die Länge des Blocks direkt vor der Rückgabe des Zeigers (ermöglichen free um zu wissen, wie viel Speicher freizugeben ist). Wenn du gibst realloc ein nicht initialisierter Zeiger, würde es denken, es sei ein gültiger Zeiger (ein Zeiger ist ein Zeiger, all realloc tun kann, ist dir zu vertrauen). Diese Implementierung würde dann versuchen, die wenigen Bytes (size_t) davor als Größe des Blocks zu interpretieren, was offensichtlich falsch wäre. Aus diesem Grund müssen Sie den Zeiger explizit auf Null setzen, damit bekannt ist, dass es sich nicht um eine gültige Adresse handelt.

    – mk12

    26. August 2012 um 23:28 Uhr


  • @Mk12: Meinten Sie, es speichert die Länge des Blocks direkt nach der Rückkehr des Zeigers (oder besser gesagt, es speichert die Länge des Blocks, auf den der Zeiger zeigt)? Entschuldigung ich bin verwirrt.

    Benutzer1607425

    27. August 2012 um 7:48 Uhr


  • @curvature: Angenommen, wir haben einen sehr alten Computer und sein Speicherplatz besteht nur aus 256 Bytes. Zeiger u size_t muss nur 1 Byte breit sein, da 1 Byte 256 verschiedene Werte enthalten kann. Wenn Sie anrufen malloc(13), es wird etwas Speicher finden. Es gibt Ihnen einen Zeiger auf die Speicheradresse zurück, sagen wir 0x5, aber es speichert tatsächlich die Zahl 13 direkt davor, in 0x4. So, wenn Sie anrufen free bei 0x5 sieht es sich das Byte davor (0x4) an, sieht, dass es die Zahl 13 enthält, und dann weiß es, dass es 13 Bytes freigeben muss (es gibt also nicht nur 0x5 frei, sondern auch 0x6, 0x7, 0x8 usw.)

    – mk12

    27. August 2012 um 18:24 Uhr


  • Also wenn du realloc B. ein nicht initialisierter Zeiger (der auf alles zeigen könnte), wird das Byte direkt davor betrachtet, und vielleicht enthält es den Wert 103, wer weiß? Nach der Zuweisung des neuen Speichers werden dort 103 Bytes freigegeben, da davon ausgegangen wird, dass Sie das, was Sie früher zugewiesen haben, jetzt möchten realloc.

    – mk12

    27. August 2012 um 18:29 Uhr


Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Mit dem gezeigten spezifischen Code gibt es kein Problem mit der anfänglichen Verwendung des Nullzeigers.

Wenn die Variable ptr nicht initialisiert ist – nicht auf 0 oder NULL gesetzt – dann ist jeder Aufruf an realloc() seine Verwendung ist gefährlich; Das Verhalten ist undefiniert und wenn Sie Glück haben, stürzt das Programm ab, aber wenn Sie Pech haben, scheint es eine Weile zu funktionieren, bis später im Programm etwas schief geht, wo es schwierig sein wird, das Problem zu erkennen in Code, der vor langer Zeit ausgeführt wurde.

Es gibt diejenigen, die argumentieren, dass es besser ist, es zu verwenden malloc() für die Erstzuteilung und realloc() danach. Der Vorschlag ist einigermaßen gerechtfertigt, nicht zuletzt, weil Sie ihn wahrscheinlich nicht verwenden würden ptr = realloc(ptr, 0); um den Speicher freizugeben, obwohl Sie dies tun könnten (also brauchen Sie es nicht wirklich malloc() oder free() Weil realloc() kann alle drei Operationen ausführen). Aber der C90-Standard erfordert realloc(0, new_size) gleichwertig zu arbeiten malloc(new_size)und ich kenne keine C-Bibliothek, die sich anders verhält (aber es könnte einige geben; ich habe nur wenige C-Bibliotheken verwendet, wenn auch meistens die am weitesten verbreiteten).


In einem allgemeineren Fall wie dem folgenden Code gibt es jedoch ein subtiles Problem mit dem Code (aber es hat nichts mit dem anfänglichen Nullzeiger zu tun):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char    *ptr = NULL;
    size_t   len = 0;
    char     buffer[256];

    while (fgets(buffer, sizeof(buffer), stdin))
    {
        size_t buflen = strlen(buffer) + 1;
        if (buflen > len)
        {
            if ((ptr = realloc(ptr, buflen)) == 0)  // Danger!
                // ... handle memory allocation failure ...
            len = buflen;
        }
        strcpy(ptr, buffer);
        // ... do something with ptr
    }
    free(ptr);
    return 0;
}

Was ist die Gefahr? Die Gefahr besteht darin, dass, wenn die zweite oder eine nachfolgende Speicherzuweisung fehlschlägt und ptr der einzige Zeiger auf den zugewiesenen Speicher ist, überschreiben Sie einfach seinen vorherigen Wert mit null. Das bedeutet, dass Sie den zugewiesenen Speicher nicht mit freigeben können ptr nicht mehr – Sie haben Speicher durchgesickert. (Bei der ersten Zuweisung war der Anfangswert 0, der überschriebene Wert war 0, und es hat sich nichts geändert; es gibt kein Speicherleck. Deshalb wurde die Schleife zum Code hinzugefügt.)

Faustregel

  • Schreibe nicht ptr = realloc(ptr, newsize);

Speichern Sie den neuen Wert in einer separaten Variablen, bis Sie ihn getestet haben.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char    *ptr = NULL;
    size_t   len = 0;
    char     buffer[256];

    while (fgets(buffer, sizeof(buffer), stdin))
    {
        size_t buflen = strlen(buffer) + 1;
        if (buflen > len)
        {
            char *new_ptr = realloc(ptr, buflen);
            if (new_ptr == 0)
                // ... handle memory allocation failure ...
            ptr = new_ptr;
            len = buflen;
        }
        strcpy(ptr, buffer);
        // ... do something with ptr
    }
    free(ptr);
    return 0;
}

Dieser Code verliert bei einem Zuordnungsfehler keinen Speicher.

Hilfsempfehlung: Verwenden Sie keine Variable namens new; es wird es schwierig machen, mit einem C++-Compiler zu kompilieren. Selbst wenn Sie jetzt nicht die Absicht haben, nach C++ zu konvertieren (und obwohl Sie wahrscheinlich die Speicherverwaltung neu schreiben würden, wenn Sie dies tun würden), ist die Verwendung des Schlüsselworts C++ nicht zielführend new als C-Variablenname … es sei denn, Sie möchten explizit die Kompilierung mit einem C++-Compiler verhindern.

  • Das war eine wirklich schöne Antwort. Leider habe ich schon einen anderen (ebenfalls guten) angenommen…

    Benutzer1607425

    27. August 2012 um 10:02 Uhr

  • Verwenden ptr = realloc(ptr,newsize ist oft in Ordnung, wenn ein Programm keine vernünftige Möglichkeit hätte, sinnvoll fortzufahren, wenn die Zuordnung fehlschlägt, obwohl realloc in eine Funktion verpackt wird, die eine Diagnose und einen Aufruf ausgibt exit im Fehlerfall kann praktischer sein, als überall Code zu verstreuen, um den Rückgabewert zu überprüfen und im Fehlerfall abzubrechen. Schade, dass es keine Funktion gibt, um eine Zuweisung zu verkleinern, ohne Zeiger darauf ungültig zu machen, da der Code einfach davon ausgehen könnte, dass eine solche Funktion immer erfolgreich sein würde (selbst wenn das System das nicht kann …

    – Superkatze

    20. Februar 2017 um 0:09 Uhr


  • … die Zuweisung aus irgendeinem Grund tatsächlich verkleinern, es könnte sie einfach so lassen, wie sie ist, und der Benutzercode müsste sich nicht darum kümmern).

    – Superkatze

    20. Februar 2017 um 0:10 Uhr

Benutzeravatar von Shahbaz
Shahbaz

Besteht eine Gefahr bei der Zuweisung von Speicher mit realloc unter Verwendung des anfänglich NULL-wertigen ptr?

Nein, das wäre genau wie ein malloc.

Wenn statt:

int* ptr = NULL;

Ich hatte das:

int* ptr; // no value given to ptr

Wäre es ein Problem, realloc mit ptr aufzurufen?

Ja, es gäbe ein Problem. Wenn realloc bekommt kein NULLes wird versuchen, den Speicher ab diesem Ort zu erweitern, oder kann versuchen zu free und malloc ein weiterer Teil der Erinnerung. Seit Nicht initialisierte Variablen können jeden Wert habenChancen sind sehr hoch, sie sind kein Wert realloc Likes. Wenn Sie Glück haben, würde Ihr Programm sofort abstürzen.

1402040cookie-checkRealloc auf NULL-wertigen (oder undefinierten) Zeiger

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

Privacy policy