2009-04-10 13 views
2

Betrachten Sie die folgende C-Code:Was passiert mit einem Malloc-Block, wenn Sie ihn nicht benutzen?

int main(){ 
    int* c; 
    c = (int*)malloc(sizeof(int)); 
    c = 0xdeadbeef; 
    free(c); 
    return 0; 
} 

Dies wird segfault, weil Sie versuchen, c zu befreien, was nicht etwas ist, das vor malloc'ed wurde. Meine Frage ist, was passiert mit dem Block, den ich gerade malloc'ed? Offensichtlich deutet c nicht mehr darauf hin, also kann es nicht verwendet werden, aber wird es immer noch als Teil der "freien" Liste betrachtet, oder ist das ein explizites Speicherleck?

Antwort

12

Es ist ein Leck. Es wird vom Betriebssystem zurückgewonnen, wenn das Programm beendet wird.

+0

Das traditionelle Unix-Speicherbereinigungsschema: Führen Sie das Programm aus und fordern Sie den Speicher zurück, wenn es beendet wird. Funktioniert nicht gut mit IDEs und Browsern und anderen Programmen, die wahrscheinlich lange laufen werden. –

1

Dieser Code wird nicht segfault, Sie c = 7; zu *c = 7;

+0

free (c) kann segfault, da free() den Junk-Pointer deref. – Michael

+0

Majd, Sie sind technisch korrekt (die beste Art von Korrekt), aber den Zeiger auf eine extrem ungültige Adresse zu ändern und zu versuchen, Dereferenzierung zu machen, war eher sein ganzer Punkt. –

+0

gut nicht, wenn Sie c = 7 zu * c = 7 ändern. Es ist ein wenig außer Thema, aber er hat Recht – Ben

10

Der Speicher zugewiesen wird sich noch ändern vorausgesetzt, ein Speicherleck verursacht. Würdest du es anders wollen? Es gibt wirklich keine Möglichkeit für den Computer/Compiler zu wissen, dass der Speicher, den Sie zugewiesen haben, zurückgefordert werden sollte. Wenn dies nicht das richtige Verhalten wäre, würde Ihr Code probabilistisch funktionieren: Sie könnten dem Code nie wirklich vertrauen.

Sie können immer wieder Punkt auf diesen Block zu einem bestimmten Zeitpunkt der Erinnerung in der Zukunft, so dass es zu befreien von unter Ihnen automatisch den Teppich ziehen würden.

+0

Nun, für andere Sprachen als C gibt es Dinge namens Müllsammler, die es für Sie tun. –

+0

Ja, aber das ist C. :) – Anthony

0

Beachten Sie, dass der von malloc() zurückgegebene Wert nicht gecastet werden muss, da die Konvertierung void* ->int* automatisch erfolgt.

Sie können auch den Aufruf wie folgt umschreiben:

c = malloc(sizeof *c); 

So, jetzt, wenn Sie c ‚s Typ zu ändern, müssen Sie sich nicht überhaupt die Zuordnung neu schreiben.

[Bearbeiten]

Es wäre auch eine gute Idee, um zu überprüfen, dass die Zuteilung erfolgreich war (das heißt c != NULL).

+0

Obwohl in Zeile 4 eine Umwandlung in (int *) erforderlich ist, erhalten Sie andernfalls eine Warnung. –

+0

Für C empfehle ich, das (int *) oder was auch immer aus dem malloc() zu lassen. Es ist nicht notwendig, und es deckt den möglichen Fehler ab, nicht eingeschlossen zu haben. –

1

Nach der malloc(), c ist eine Variable, eine Speicheradresse zu halten. In diesem Fall hat c den Wert der Speicheradresse des ersten Byte, das Sie zugewiesen haben. Wenn Sie sagen: c = 7, sagen Sie "c zeigt jetzt auf Speicheradresse '7'". Da in diesem Fall die Speicheradresse "7" Ihrem Prozess nicht zugewiesen wurde, können Sie sie nicht freigeben(). Theoretisch ist es möglich, dass die Speicheradresse "7" (oder 0xa73c930bf oder was auch immer Sie festgelegt haben) tatsächlich Ihrem Prozess zugewiesen wurde. In diesem Fall wäre das free() erfolgreich.

Nachdem Sie den Wert von c zu ändern, jedoch wird der Speicher noch zugeordnet. Ihr Prozess hat immer noch den Speicher und seine Daten - Sie haben gerade den Zeiger verloren. Sie wissen nicht, wo der Speicher beginnt.

Dies ist eine gute Situation. In C könnten verschiedene Variablen auf diese Speicheradresse verweisen. Möglicherweise haben Sie sogar ein "int" anstelle eines "int *", das diese Speicheradresse speichert. C versucht nicht zu verfolgen, ob Sie diese bestimmte Speicheradresse gespeichert haben oder nicht - was eigentlich nur eine Zahl ist. Dies zu tun wäre eine unmögliche Aufgabe, da jeder Versuch, den Überblick zu behalten, einige Flexibilität verlieren würde, die C-Zeiger bieten.

So Ihre Frage zu beantworten: Da Sie keine Möglichkeit haben, den Überblick über den Wert von c zu halten, hat dieses Programm einen Speicherverlust.Und damit meine ich, dass Sie Speicher haben, der von Ihrem Programm nicht genutzt werden kann und Ressourcen verschwendet.

Wenn das Programm jedoch beendet wird, wird der gesamte Speicher, der Ihrem Prozess zugewiesen wurde, freigegeben und für andere Programme verfügbar gemacht.

+0

Es kann möglich sein, eine Adresse in einem int zu speichern, aber es ist eine schlechte Idee. Es gibt keine Garantie, dass int und int * die gleiche Größe und die gleiche Darstellung haben. –

+0

Denken Sie daran, Sie können versuchen,() es freizugeben, und die Implementierung kann tatsächlich den freien() Code ausführen, den Speicher ändern. Dies ist fast immer eine schlechte Sache, da es etwas nicht gut definierte auf einem Stück Speicher gemacht hat, der für den Rest des Programms wichtig sein könnte. –

+0

Wenn Sie einen Zeiger in einer Integer-Variablen speichern möchten, verwenden Sie intptr_t. intptr_t ist groß genug, um einen Zeiger eines beliebigen Typs zu speichern. – sigjuice

1

Bitte flame mich nicht, aber ich bin nicht klar, was der Punkt Ihrer Frage ist. Was Sie tun, steht in direktem Konflikt mit dem, was die Sprache für Sie vorhat. Es ist gleichbedeutend mit der Aussage "Was würde passieren, wenn ich den Benzintank meines Autos mit Feuerlöschmittelflüssigkeit füllen würde, nur weil die Düse des Feuerlöschers zufällig in die Gasbehälteröffnung passt".
Ich versuche nicht ein Idiot zu sein, ich bin einfach nicht klar, warum diese spezielle Frage? Ich kann mir eine endlose Reihe von Fragen vorstellen, die die Vorteile von Zeigern ausnutzen und darüber nachdenken, wie viele Wege es gibt, sie falsch zu verwenden, was zu einem Anwendungsfehler führt. Gibt es etwas, was Sie erreichen möchten, das Ihr Code * tun würde, wenn Sie nur den richtigen Weg finden könnten? Oder fragen Sie sich, ob es einen internen Mechanismus gibt, der Ihre Zeiger verfolgt und Ihnen hilft, diesen Speicher wiederzuerlangen, wenn Sie ihn versehentlich aus den Augen verlieren? (Wenn ja, wurde diese Frage oben beantwortet).

1

Nun, ich bin der segfault, den Sie an dieser Stelle erhalten, nicht, weil Sie versuchen, Speicher freizugeben, den Sie zuvor nicht zugewiesen haben. Der Segfault tritt auf, weil Sie versuchen, eine Speicheradresse zu referenzieren, für die das Betriebssystem Ihnen keine Berechtigung erteilt hat Sie zu Ihnen (das ist die Definition eines Segmentierungsfehlers).

Einige Experimente, sagen Sie Ihren Beispielcode in valgrind laufen würde, können Sie dies als Ausgabe erhalten würde:

 
    ==6945== Invalid free()/delete/delete[] 
    ==6945== at 0x402265C: free (vg_replace_malloc.c:323) 
    ==6945== by 0x80483D5: main (bla.c:6) 
    ==6945== Address 0x7 is not stack'd, malloc'd or (recently) free'd 
    ==6945== 
    ==6945== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 11 from 1) 
    ==6945== malloc/free: in use at exit: 4 bytes in 1 blocks. 
    ==6945== malloc/free: 1 allocs, 1 frees, 4 bytes allocated. 

Das ist also ein Speicherleck ‚pur sang‘. Angenommen, Sie würden den Code so ändern, dass der Zeiger, den Sie zu befreien versuchen, "nahe" dem zugewiesenen Zeiger ist (damit das Betriebssystem weiterhin weiß, dass Sie Zugriff darauf haben, gewährt das Betriebssystem keinen Speicherzugriff auf Bytegrenzen). Sagen wir den Code wie folgt ändern:

int main(){ 
    int* c; 
    c = (int*)malloc(sizeof(int)); 
    c++; 
    free(c); 
    return 0; 
} 

Wenn diese Anwendung ausgeführt wird Sie nicht mehr einen Segmentation Fault bekommen würde (vom Kernel emittiert), aber eine Warnung von glibc (der Besitzer von malloc() und free())

 
    [email protected]:/tmp$ ./a.out 
    *** glibc detected *** ./a.out: free(): invalid pointer: 0x0804a00c *** 
    ... followed by a trace 

So sind Sie versuchen, etwas Speicher frei von denen der Kernel weiß, dass es dir gehört, aber von denen glibc erinnern kann es dir nicht verteilen. Wenn Sie dies in valgrind laufen (was die freie durch Ersetzen arbeitet(), malloc(), realloc(), ... Funktion in libc und Durchführung Buchhaltung auf eigene) Sie diese Ausgabe erhalten würde:

 
    ==6955== Invalid free()/delete/delete[] 
    ==6955== at 0x402265C: free (vg_replace_malloc.c:323) 
    ==6955== by 0x80483D2: main (bla.c:5) 
    ==6955== Address 0x418a02c is 0 bytes after a block of size 4 alloc'd 
    ==6955== at 0x4022AB8: malloc (vg_replace_malloc.c:207) 
    ==6955== by 0x80483C0: main (bla.c:3) 
    ==6955== 
    ==6955== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 11 from 1) 
    ==6955== malloc/free: in use at exit: 4 bytes in 1 blocks. 
    ==6955== malloc/free: 1 allocs, 1 frees, 4 bytes allocated. 
+0

+1 für FSM in Ihrer Bash-Eingabeaufforderung. –