Kann ich davon ausgehen, dass das Aufrufen von realloc mit einer kleineren Größe den Rest freigibt? [duplicate]

Lesezeit: 6 Minuten

Benutzeravatar von qdii
qdii

Betrachten wir dieses sehr kurze Code-Snippet:

#include <stdlib.h>

int main()
{
    char* a = malloc(20000);
    char* b = realloc(a, 5);

    free(b);
    return 0;
}

Nachdem ich die Manpage für realloc gelesen hatte, war ich mir nicht ganz sicher, ob die zweite Zeile dazu führen würde, dass die 19995 zusätzlichen Bytes freigegeben würden. Um die Manpage zu zitieren: The realloc() function changes the size of the memory block pointed to by ptr to size bytes.aber kann ich aufgrund dieser Definition sicher sein, dass der Rest befreit wird?

Ich meine, der Block zeigte auf b enthält sicherlich 5 freie Bytes, also würde es für einen faulen konformen Zuordner ausreichen, einfach nichts für die Realloc-Leitung zu tun?

Hinweis: Der von mir verwendete Allocator scheint die 19 995 zusätzlichen Bytes freizugeben, wie von valgrind beim Auskommentieren von gezeigt free(b) Linie :

==4457== HEAP SUMMARY:
==4457==     in use at exit: 5 bytes in 1 blocks
==4457==   total heap usage: 2 allocs, 1 frees, 20,005 bytes allocated

Ja, garantiert durch den C-Standard, wenn das neue Objekt zugeordnet werden kann.

(C99, 7.20.3.4p2) “Die realloc-Funktion hebt die Zuordnung des alten Objekts auf, auf das ptr zeigt, und gibt einen Zeiger auf ein neues Objekt zurück, das die durch size angegebene Größe hat.”

  • Der C99-Entwurf erwähnt nie etwas dagegen, dass realloc nichts tut, wenn die alte Größe gleich der neuen ist. Ich denke, alle Implementierungen verstoßen dagegen?

    – qdii

    5. März 2012 um 23:07 Uhr

  • C99 7.20.3.4 §4: Die Funktion realloc gibt einen Zeiger auf das neue Objekt zurück (der denselben Wert haben kann wie ein Zeiger auf das alte Objekt).

    – Christoph

    5. März 2012 um 23:11 Uhr

Benutzeravatar von Adam Liss
Adam Liss

Ja – wenn es gelingt.

Ihr Code-Snippet zeigt einen bekannten, schändlichen Fehler:

char* b = (char*) realloc(a, 5);

Wenn dies gelingt, wird der Speicher, der zuvor zugewiesen wurde a wird befreit, und b zeigt auf 5 Bytes Speicher, die den ursprünglichen Block überlappen können oder nicht.

Jedochwenn der Anruf fehlschlägt, b wird seinnullaber a zeigt immer noch auf seinen ursprünglichen Speicher, der immer noch gültig ist. In diesem Fall müssen Sie free(a) um die Erinnerung freizugeben.

Es ist noch schlimmer, wenn Sie die gängige (gefährliche) Redewendung verwenden:

a = realloc(a, NEW_SIZE);     // Don't do this!

Wenn der Anruf an realloc scheitert, ein wird null und sein ursprünglicher Speicher wird verwaist, wodurch er unwiederbringlich verloren geht, bis Ihr Programm beendet wird.

  • Sie haben Recht. Ich hatte gesehen, wie sich cppcheck darüber beschwerte. Ich denke, das ist ein Killer für Prüfungstests, aber wirklich, passiert das jemals?

    – qdii

    5. März 2012 um 23:00 Uhr

  • @qdii: In der eingebetteten Welt sind alle Wetten geschlossen; außerdem kann das betriebssystem einige beschränkungen erzwingen, zB für den virtuellen speicherplatz via ulimit

    – Christoph

    5. März 2012 um 23:05 Uhr

  • Ja, das passiert wirklich in der realen Welt. Warum stürzt Ihrer Meinung nach so viel beschissene Software ab, wenn Ihnen der Speicher ausgeht (auf einem System ohne Swap), anstatt Ihnen eine Meldung zu geben, dass sie die angeforderte Operation aufgrund von Speichermangel nicht ausführen kann?

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    5. März 2012 um 23:35 Uhr

Benutzeravatar von Christoph
Christoph

Dies hängt von Ihrer libc-Implementierung ab. Alles Folgende ist konformes Verhalten:

  • Nichts tun, dh die Daten dort belassen, wo sie sind, und den alten Block zurückgeben, möglicherweise die jetzt unbenutzten Bytes für weitere Zuweisungen wiederverwenden (Afaik, eine solche Wiederverwendung ist nicht üblich)
  • Kopieren der Daten in einen neuen Block und Freigeben des alten Blocks zurück an das Betriebssystem
  • Kopieren der Daten in einen neuen Block und Beibehalten des alten für weitere Zuordnungen

Es ist auch möglich

  • gibt einen Nullzeiger zurück, wenn kein neuer Block zugewiesen werden kann

Auch in diesem Fall bleiben die alten Daten wo sie sind, was zu Speicherlecks führen kann, zB wenn der Rückgabewert von realloc() überschreibt die einzige Kopie des Zeigers auf diesen Block.

Eine vernünftige libc-Implementierung verwendet eine Heuristik, um zu bestimmen, welche Lösung am effizientesten ist.

Denken Sie auch daran, dass sich diese Beschreibung auf der Implementierungsebene befindet: Semantisch realloc() gibt das Objekt immer frei, solange die Zuordnung nicht fehlschlägt.

  • In Bezug auf “afaik, eine solche Wiederverwendung ist nicht üblich”, habe ich noch nie von einer Implementierung in der realen Welt gehört, die die alte Zuordnung beibehalten und den Schwanz nicht für die Wiederverwendung freigibt. Dies wäre ein pathologisch schlechtes, wenn auch legales Verhalten.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    5. März 2012 um 23:37 Uhr

  • Gibt es Standards, die dieses Verhalten definieren, oder liegt es wirklich an der libc-Implementierung?

    – rr-

    31. Januar 2015 um 18:14 Uhr

  • @rr-: der C-Standard sowie POSIX überlässt es der Implementierung; Mir ist keine Spezifikation bekannt, die das Verhalten definiert, aber es könnte im libc-Handbuch dokumentiert sein

    – Christoph

    31. Januar 2015 um 19:04 Uhr

Es scheint unwahrscheinlich, dass 19995 Bytes freigegeben wurden. Wahrscheinlicher ist, dass realloc den 20000-Byte-Block durch einen anderen 5-Byte-Block ersetzt hat, dh der 20000-Byte-Block wurde freigegeben und ein neuer 5-Byte-Block zugewiesen.

Die realloc-Funktion hat den folgenden Vertrag:

void* result = realloc(ptr, new_size)

  • Wenn das Ergebnis NULL ist, ist ptr immer noch gültig und unverändert.
  • Wenn das Ergebnis nicht NULL ist, ist ptr jetzt ungültig (als ob es freigegeben worden wäre) und darf nie wieder verwendet werden. Ergebnis ist jetzt ein Zeiger auf genau new_size Datenbytes.

Die genauen Details dessen, was unter der Haube vor sich geht, sind implementierungsspezifisch – zum Beispiel kann result gleich ptr sein (aber zusätzliches Leerzeichen hinter new_size darf nicht mehr berührt werden) und realloc kann free aufrufen oder seine eigene interne freie Repräsentation durchführen. Das Wichtigste ist, dass Sie als Entwickler nicht länger für ptr verantwortlich sind, wenn realloc nicht null zurückgibt, und dass Sie immer noch dafür verantwortlich sind, wenn realloc NULL zurückgibt.

  • Außerdem ist die Übergabe von new_size = 0 dasselbe wie free (ptr) und gibt NULL zurück. Das wäre ein Fall, in dem result == NULL und ptr nicht mehr gültig ist.

    – gnasher729

    31. März 2014 um 21:19 Uhr

  • Außerdem ist die Übergabe von new_size = 0 dasselbe wie free (ptr) und gibt NULL zurück. Das wäre ein Fall, in dem result == NULL und ptr nicht mehr gültig ist.

    – gnasher729

    31. März 2014 um 21:19 Uhr

1387390cookie-checkKann ich davon ausgehen, dass das Aufrufen von realloc mit einer kleineren Größe den Rest freigibt? [duplicate]

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

Privacy policy