2015-12-15 7 views
10

Ich habe in Vorlesungen unterrichtet, dass das Rufen von free() auf einem Zeiger zweimal wirklich, wirklich schlecht ist. Ich weiß, dass es eine gute Übung ist, einen Zeiger auf NULL zu setzen, gleich nachdem er freigegeben wurde.Zweimal auf einen Zeiger rufen

Allerdings habe ich noch nie eine Erklärung dafür gehört, warum das so ist. Von dem, was ich verstehe, die Art und Weise malloc() funktioniert, sollte es technisch verfolgen die Zeiger, die es zugewiesen hat und Ihnen zu verwenden. Also warum weiß es nicht, ob ein Zeiger, den es durch free() erhält, schon freigegeben wurde oder nicht?

Ich würde gerne verstehen, was intern passiert, wenn Sie free() an einem Speicherort aufrufen, der zuvor bereits freigegeben wurde.

+0

der Zeiger ist nur, wo Sie die direccion der Daten anotate, aber was Sie tatsächlich frei ist die Daten selbst – Netwave

+0

@DanielSanchez Ja, aber da malloc Sie diesen Zeiger wirft, sollte nicht die Speicherfreigabe/Zuteilung konstruieren noch Beachten Sie, dass es die Position freigegeben hat, auf die der Zeiger vorher zeigt? – Joe

+0

Wenn Sie den Zeiger nicht verwenden, nachdem Sie "frei" sind, dann ist es nicht sinnvoll, ihn auf "NULL" zu setzen. Und * wenn * Sie den Zeiger verwenden, nachdem Sie "frei" haben, haben Sie * undefiniertes Verhalten *, egal, ob Sie es auf "NULL" setzen oder nicht. Natürlich, wenn Sie * für "NULL" überprüfen, dann hilft es, aber die Notwendigkeit, einen Zeiger auf "NULL" zu setzen, ist nicht etwas, das Sie absolut tun müssen, tun Sie es von Fall zu Fall abhängig davon, wie Sie verwenden der Zeiger. –

Antwort

7

Wenn Sie malloc verwenden, sagen Sie dem PC, dass Sie nur einen Speicherort auf dem Heap für Sie reservieren möchten. Der Computer gibt einen Zeiger auf das erste Byte des adressierten Speicherplatzes zurück.

Wenn Sie free verwenden, sagen Sie dem Computer, dass Sie diesen Speicherplatz nicht mehr benötigen, daher markiert er diesen Speicherplatz als für andere Daten verfügbar.

Der Zeiger zeigt immer noch auf diese Speicheradresse. An diesem Punkt kann derselbe Speicherplatz im Heap von einem anderen malloc-Aufruf zurückgegeben werden. Wenn Sie free ein zweites Mal aufrufen, werden Sie nicht die vorherigen Daten zu befreien, aber die neuen Daten, und dies kann für Ihr Programm nicht gut sein;)

+0

Danke für die Erklärung! – Joe

3

Ihre erste Frage zu beantworten,

So Warum weiß es nicht, ob ein Zeiger, den es durch free() erhält, schon freigegeben wurde oder nicht?

weil die Spezifikation für malloc() in C-Standard ist dies nicht vorschreiben. Wenn Sie malloc() oder Familie von Funktionen aufrufen, was es tut, ist Ihnen einen Zeiger zurückzugeben und intern speichert es die Größe des Speicherplatzes, der in diesem Zeiger zugeordnet ist. Das ist der Grund, free() benötigt keine Größe, um den Speicher zu bereinigen.

Auch einmal free()-d, was passiert mit der tatsächlich zugewiesenen Speicher ist immer noch implementierung abhängig. Der Aufruf free() ist nur ein Marker, um darauf hinzuweisen, dass der zugewiesene Speicher nicht mehr von dem Prozess verwendet wird und wenn erforderlich zurückgewonnen und neu zugewiesen werden kann. Also ist es zu diesem Zeitpunkt sehr überflüssig, den zugewiesenen Zeiger im Auge zu behalten. Es wird eine unnötige Belastung für das OS sein, die Backtracks alle zu behalten.

Zu Debuggingzwecken können jedoch einige Bibliotheksimplementierungen diese Aufgabe für Sie ausführen, wie DUMA oder dmalloc und nicht zuletzt das memcheck-Tool von Valgrind. Jetzt

, technisch, die C Norm legt keine jedes Verhalten, wenn Sie free() auf eine bereits frei ed Zeiger nennen. Es ist undefined behavior.

C11, Kapitel §7.22.3.3, free() Funktion

[...], wenn das Argument nicht einen Zeiger früher durch eine Speicherverwaltung Funktion, oder wenn der Raum durch einen Aufruf free() oder realloc(), die ausgeplant zurück überein wurde Verhalten ist nicht definiert.

+1

Danke, das macht es ziemlich deutlich – Joe

2

Wenn Sie malloc aufrufen, erhalten Sie einen Zeiger. Die Laufzeitbibliothek muss den malloc Ed-Speicher verfolgen. In der Regel speichert malloc die Speicherverwaltungsstrukturen nicht getrennt vom malloc Ed-Speicher, sondern an einer Stelle. So nimmt ein malloc für x Bytes tatsächlich x + n Bytes, wobei ein mögliches Layout darin besteht, dass die ersten n Bytes eine verkettete Listenstruktur mit Zeigern zum nächsten (und vielleicht vorherigen) zugewiesenen Speicherblock enthalten.

Wenn Sie free ein Zeiger dann free die Funktion gehen können durch sie Managementstrukturen interne Speicher ist und prüfen, ob der Zeiger Du in einem gültigen Zeiger, die malloc ed war, ist übergeben. Nur dann könnte es auf die versteckten Teile des Speicherblocks zugreifen. Aber diese Überprüfung wäre sehr zeitaufwendig, besonders wenn Sie viel zuweisen. So nimmt free einfach an, dass Sie einen gültigen Zeiger übergeben. Das bedeutet, dass es direkt auf die verborgenen Teile des Speicherblocks zugreift und annimmt, dass die verknüpften Listenzeiger dort gültig sind.

Wenn Sie free ein Block zweimal dann könnten Sie das Problem haben, dass jemand eine neue malloc tat, bekam den Speicher, den Sie gerade befreit, überschreibt es und die zweite free liest ungültige Zeiger von ihm.

Das Setzen eines free d-Zeigers auf NULL ist eine gute Übung, weil es das Debuggen erleichtert. Wenn Sie auf free d Speicher zugreifen, stürzt Ihr Programm möglicherweise ab, aber es könnte auch nur verdächtige Werte lesen und später möglicherweise abstürzen. Das Finden der Grundursache kann dann schwierig sein. Wenn Sie free d Zeiger auf NULL setzen, stürzt Ihr Programm sofort ab, wenn Sie versuchen, auf den Speicher zuzugreifen. Das hilft massiv beim Debuggen.

0

C-Standard sagt nur, dass Aufruf free zweimal auf einen Zeiger von malloc zurückgegeben und seine Familienfunktion undefined Verhalten aufrufen. Es gibt keine weitere Erklärung, warum es so ist.
Aber, warum es schlecht ist, wird here erklärt:

Zweimal The Same Chunk Befreit

Um zu verstehen, was diese Art von Fehler kann dazu führen, sollten wir uns daran erinnern, wie der Speichermanager arbeitet normal. Häufig speichert es die Größe des zugewiesenen Chunks unmittelbar vor dem Chunk selbst im Speicher. Wenn wir den Speicher freigegeben haben, wurde dieser Speicherblock möglicherweise erneut von einer anderen malloc()-Anforderung zugewiesen, und somit wird dieser doppelfreie tatsächlich den falschen Speicherblock freigeben - was dazu führt, dass wir irgendwo in unserer Anwendung einen baumelnden Zeiger haben. Solche Fehler tendieren dazu, sich viel später als die Stelle im Code, an der sie aufgetreten sind, zu zeigen. Manchmal sehen wir sie überhaupt nicht, aber sie lauern immer noch herum und warten auf eine Gelegenheit, ihre hässlichen Köpfe aufzurichten.

Ein weiteres Problem, das occure könnte, ist, dass diese Doppelfrei nach dem befreiten Brocken verschmolzen wurde zusammen mit dem benachbarten freien Stücken getan wird, um eine größere freie Klumpen zu bilden, und dann die größeren Brocken wurden neu zugewiesen. Wenn wir in diesem Fall zum zweiten Mal versuchen, unseren Chunk free() zu testen, werden wir nur einen Teil des Speicherblocks freigeben, den die Anwendung gerade verwendet. Dies wird zu noch unerwarteteren Problemen führen.