2010-04-22 6 views
9

Ich habe irgendwo gelesen, dass es verheerend ist, free zu verwenden, um ein Objekt loszuwerden, das nicht durch den Aufruf malloc erstellt wurde, ist das wahr? Warum?Warum genau sollte ich free() nicht für Variablen aufrufen, die nicht von malloc() zugewiesen wurden?

+1

Nur eine geringfügige semantische Korrektur: Es gibt keine "Objekte" in C. Besser wäre "Variable" oder "Struktur". –

+8

@Chris Das K & R-Buch, sagt es (irgendwo am Ende), dass Objekt ist jeder Speicherort, der durch einen Namen verwiesen werden kann (oder so erinnere ich mich, ich kann meine Kopie nicht finden) – Tom

+7

@Chris Cooper: Nur weil manche Leute sagen dass C nicht "objektorientiert" ist, bedeutet nicht, dass es in dieser Sprache kein Objekt gibt. Aus der C-Spezifikation: "Objekt: Bereich des Datenspeichers in der Ausführungsumgebung, dessen Inhalt Werte darstellen kann." Hier hast du es. –

Antwort

12

Das ist undefiniertes Verhalten - versuchen Sie es nie.

Mal sehen, was passiert, wenn Sie versuchen, free() eine automatische Variable. Der Heap-Manager muss daraus ableiten, wie er den Besitz des Speicherblocks übernimmt. Dazu muss entweder eine separate Struktur verwendet werden, die alle zugewiesenen Blöcke auflistet, und das ist sehr langsam und wird nur selten verwendet, oder ich hoffe, dass sich die erforderlichen Daten in der Nähe des Blockanfangs befinden.

Letzteres wird oft verwendet und so soll es funktionieren. Wenn Sie malloc() aufrufen, weist der Heap-Manager einen etwas größeren Block zu, speichert die Service-Daten am Anfang und gibt einen Offset-Zeiger zurück. Smth wie:

void* malloc(size_t size) 
{ 
     void* block = tryAlloc(size + sizeof(size_t)); 
     if(block == 0) { 
      return 0; 
     } 
     // the following is for illustration, more service data is usually written 
     *((size_t*)block) = size; 
     return (size_t*)block + 1; 
} 

dann free() werden versuchen, diese Daten zugreifen, indem Sie den übergebenen Zeiger Verrechnung aber wenn der Zeiger auf eine automatische Variable unabhängig von Daten befinden wird, wo es Servicedaten finden erwartet. Daher undefiniertes Verhalten. Viele Male Service-Daten werden von free() für Heap-Manager geändert, um den Besitz des Blocks zu übernehmen - wenn also der übergebene Zeiger auf eine automatische Variable ist, wird nicht verwandter Speicher geändert und gelesen.

Implementierungen können variieren, Sie sollten jedoch niemals spezifische Annahmen treffen. Rufen Sie free() nur an Adressen an, die von malloc() Familienfunktionen zurückgegeben werden.

+0

mehr bitte ... :) – ultrajohn

+1

Sie müssten 'versuchenAlloc (size + sizeof (size_t))' Platz für die 'size_t 'vor haben. –

+0

@Chris Lutz: Danke, habe das behoben. – sharptooth

6

It is undefiniertes Verhalten. Und logisch, wenn das Verhalten nicht definiert ist, können Sie nicht sicher sein, was passiert ist und ob das Programm noch ordnungsgemäß funktioniert.

+4

+1, aber ich kann nasal dämonen haz? –

+0

aber, wie und warum führt es zu einem undefinierten Verhalten? – ultrajohn

+3

Warum? Weil der Standard es sagt. Wie? Ich weiß es nicht. Wenn wir wüssten, wie es passiert ist, wäre es nicht undefiniert. –

9

Nach dem Standard ist es "undefined Verhalten" - d. H. "Alles kann passieren". Das sind normalerweise schlechte Dinge.

In der Praxis: free Ein Zeiger bedeutet, den Heap zu ändern. C runtime validiert praktisch nie, wenn der übergebene Zeiger vom Heap kommt - das wäre zu zeit- oder speicherintensiv. Kombinieren Sie diese beiden Fakten, und Sie bekommen "freie (nicht-malloced-ptr) wird irgendwo etwas schreiben" - die Ergebnisse können einige von "Ihren" Daten hinter Ihrem Rücken, eine Zugriffsverletzung oder Trashing wichtige Laufzeitstrukturen, wie eine Rücksendeadresse auf dem Stapel.

Beispiel: Eine katastrophale Szenario:
Ihr Haufen ist als eine einfache Liste von freien Blöcken implementiert. malloc bedeutet, einen geeigneten Block aus der Liste zu entfernen, free bedeutet, dass er erneut zur Liste hinzugefügt wird. (eine typische wenn triviale Implementierung)

Sie frei() einen Zeiger auf eine lokale Variable auf dem Stapel. Sie sind "glücklich", weil die Änderung in irrelevanten Stapelspeicherplatz geht. Allerdings ist ein Teil des Stapels jetzt auf Ihrer freien Liste.

Aufgrund des Allokatorentwurfs und der Zuordnungsmuster ist es unwahrscheinlich, dass malloc diesen Block zurückgibt. Später, in einem völlig nicht verwandten Teil des Programms, erhalten Sie diesen Block tatsächlich als malloc-Ergebnis, indem Sie einige lokale Variablen auf den Stack schreiben, und wenn Sie einen wichtigen Zeiger zurückgeben, enthält er Müll und Ihre App stürzt ab. Symptome, Repro und Ort sind völlig unabhängig von der eigentlichen Ursache.

Debuggen Sie das.

5

Einige Leute haben hier darauf hingewiesen, dass dies "undefiniertes Verhalten" ist. Ich werde weiter gehen und sagen, dass dies bei einigen Implementierungen entweder Ihr Programm zum Absturz bringen oder Datenbeschädigung verursachen wird. Es hat damit zu tun, wie "malloc" und "free" implementiert werden.

Eine Möglichkeit, malloc/free zu implementieren, besteht darin, vor jeder zugewiesenen Region einen kleinen Header zu setzen. In einer Malloc'd-Region würde dieser Header die Größe der Region enthalten. Wenn die Region freigegeben wird, wird diese Kopfzeile überprüft und die Region wird der entsprechenden Freelist hinzugefügt. Wenn Ihnen das passiert, sind das schlechte Nachrichten. Wenn Sie beispielsweise ein auf dem Stapel zugeordnetes Objekt freigeben, befindet sich plötzlich ein Teil des Stapels in der Freiliste. Dann könnte malloc diese Region als Antwort auf einen zukünftigen Aufruf zurückgeben, und Sie werden Daten über Ihren gesamten Stack kritzeln. Eine andere Möglichkeit ist, dass Sie eine Zeichenkette konstant freigeben. Wenn sich diese Zeichenfolgenkonstante im schreibgeschützten Speicher befindet (dies ist oft der Fall), würde diese hypothetische Implementierung einen segfault und einen Absturz entweder nach einem späteren malloc verursachen oder wenn free das Objekt seiner Freelist hinzufügt.

Dies ist eine hypothetische Implementierung, über die ich spreche, aber Sie können Ihre Vorstellungskraft verwenden, um zu sehen, wie es sehr, sehr falsch gehen könnte. Einige Implementierungen sind sehr robust und nicht anfällig für diese Art von Benutzerfehlern. In einigen Implementierungen können Sie sogar Umgebungsvariablen festlegen, um diese Fehlertypen zu diagnostizieren. Valgrind und andere Tools werden diese Fehler ebenfalls erkennen.

+0

undefined Verhalten als offiziell definiert - "Verhalten, bei Verwendung eines nicht tragbaren oder fehlerhaften Programmkonstrukts oder von fehlerhaften Daten, für die diese Internationale Norm keine Anforderungen stellt". Da der Standard keine Anforderungen enthält, müssen bei einigen Implementierungen verschiedene Implementierungen und das Programm _mai_ crash auftreten. –

0

Es wäre sicherlich möglich für eine Implementierung von malloc/free eine Liste der Speicherblöcke zu halten thats, zugeteilt worden und im Fall der Benutzer versucht, einen Block zu befreien, die nicht in dieser Liste nichts tun.

Da jedoch der Standard besagt, dass dies keine Voraussetzung ist, behandelt die meiste Implementierung alle Zeiger, die als gültig erkannt werden.

0

Bitte schauen Sie sich an, was . malloc() und free() auf einer konformen gehostet C-Implementierung sind nach Standards gebaut. Die Standards sagen, dass das Verhalten des Aufrufens von free() auf einem Heap-Block, der nicht von malloc() zurückgegeben wurde (oder etwas, das es umhüllt, z. B. calloc()), nicht definiert ist.

Dies bedeutet, es kann tun, was auch immer Sie wollen es tun, vorausgesetzt, dass Sie die notwendigen Änderungen an free() auf eigene Faust vornehmen. Sie werden nicht Pause der Standard durch das Verhalten von free() auf Blöcken machen nicht zugeteilt durch malloc()konsistente und möglicherweise sogar nützlich.

Tatsächlich könnte es Plattformen geben, die () dieses Verhalten definieren. Ich kenne keine, aber es könnte welche geben. Es gibt mehrere malloc() - Implementierungen, die das Protokollieren des Ereignisses fälschlicherweise durchfallen lassen. Aber das ist Implementierung, nicht Standards definiert Verhalten.

undefiniert einfach bedeutet, zählt nicht auf jede Art von konsistentem Verhalten, wenn Sie es selbst implementieren ohne jedes definiertes Verhalten zu brechen. Schließlich Implementierung definiert bedeutet nicht immer definiert durch das Host-System. Viele Programme verlinken gegen (und versenden) uclibc.In diesem Fall ist die Implementierung in sich abgeschlossen, konsistent und tragbar.

3

Streng genommen ist dies nicht wahr. calloc() und realloc() sind ebenfalls gültige Objektquellen für free(). ;)

+0

Genau genommen sind Sie richtig. Calloc() und Realloc() sind jedoch nur bequeme Schnittstellen zu malloc(). Wenn man eine Plattform mit nur malloc() und memcpy() bekommt, könnte man calloc() und realloc() in wenigen Minuten implementieren. –

+3

Nicht genau. Um die Daten für Realloc zu kopieren, müssen Sie die Größe des alten Speicherblocks kennen. – Secure

Verwandte Themen