13 Stimmen

Warum muss ich free für einen Zeiger verwenden, aber nicht für eine normale Deklaration?

Warum muss ich free() verwenden, wenn ich einen Zeiger deklariere wie z.B.:

int *temp = (int*)malloc(sizeof(int))
*temp = 3;

aber nicht, wenn ich es tue:

int temp = 3;

32voto

Alnitak Punkte 324207

Normale Deklarationen werden auf den Stapel gelegt. Wenn die Funktion zurückkehrt, kehrt der Stack-Zeiger zu dem Wert zurück, den er vor dem Aufruf der Funktion hatte, so dass der Speicher automatisch zurückgewonnen wird.

Malloc-basierte Deklarationen werden vom "Heap" zugewiesen, was den Programmierer dazu zwingt, Zuweisungen und Freigaben zu verwalten.

1 Stimmen

Gilt dies auch für global deklarierte Variablen?

1 Stimmen

Nein, AFAICR global deklarierte Variablen erhalten Platz im "Datensegment" - einem separaten Speicherblock, der vom Betriebssystem auf der Grundlage der bekannten Speicheranforderungen der Anwendung reserviert wird.

0 Stimmen

Globale Variablen werden "freigegeben", wenn das Modul, in dem sie enthalten sind, entladen wird oder der Prozess beendet wird.

12voto

Bill the Lizard Punkte 384619

Sie müssen nicht immer free für einen Zeiger verwenden, nur für solche, die mit malloc deklariert wurden. Sie können einen Zeiger deklarieren, der auf einen Speicherplatz auf dem Stack zeigt

int a = 3;
int* p = &a;

und dieser Speicher (zusammen mit dem Zeiger) wird auch automatisch entsorgt, wenn er den Geltungsbereich verlässt. Bei der Verwendung von malloc wird derselbe Speicher auf dem Heap zugewiesen, so dass Sie die Bereinigung manuell vornehmen müssen.

0 Stimmen

Nur zur Klarstellung: Zeigervariablen werden auf dem Stack erstellt und enthalten eine Speicheradresse für die auf dem Heap zugewiesenen Daten. Auf einem 32-Bit-System benötigen sie 4 Byte auf dem Stack und auf einem 64-Bit-System 8 Byte auf dem Stack.

0 Stimmen

@Brian: Danke für die Klarstellung. Ich denke, dass meine Antwort durch die Änderungen technisch genauer geworden ist.

8voto

Brian R. Bondy Punkte 325712

Denn die Sprache lässt Ihnen die Wahl zwischen dem Stack und dem Heap.

Gründe, warum man sich zwischen Stack und Heap entscheiden sollte:

  • Variablen auf dem Heap geben sich absichtlich nicht selbst frei, damit Sie sie über den Bereich Ihres Codeblocks oder Ihrer Funktion hinaus verwenden können.
  • Es ist effizienter, mit dem Stack zu arbeiten als mit dem Heap
  • Bei den meisten Compilern kann die Größe eines Objekts oder Arrays auf dem Stack nicht zur Laufzeit bestimmt werden, so dass hier der Heap verwendet wird.

Warum der Heap nicht automatisch freigegeben werden kann:

Denn es gibt keine Möglichkeit zu wissen, wann man mit dem Speicher fertig ist. Es gibt Möglichkeiten, die Garbage Collection zu emulieren, aber dies beinhaltet, wenn Sie keine Variablen mehr auf dem Stack und Heap haben, die einen Zeiger auf die Daten auf dem Heap halten.

Mehr zu Stack vs. Heap:

In der Sprache C können Sie wählen, ob Sie Ihre Variablen auf dem Stack oder dem Heap definieren wollen.

  • Variablen auf dem Stack werden automatisch freigegeben, wenn sie aus dem Umfang .
  • Variablen auf dem Heap werden nicht automatisch freigegeben.

malloc erstellen Variablen auf dem Heap. Eine einfache Deklaration wie int x; erzeugt eine Variable auf dem Stack.

Siehe weitere Lektüre über Stack vs Heap in meiner Antwort hier .

Zeiger:

Nur zur Klarstellung: Zeigervariablen werden auf dem Stack erstellt und enthalten eine Speicheradresse für die auf dem Heap zugewiesenen Daten. Auf einem 32-Bit-System benötigen sie 4 Byte auf dem Stack und auf einem 64-Bit-System 8 Byte auf dem Stack.

0 Stimmen

Nur aus Interesse, weil ich noch nie nachgeschaut habe - erstellt ein Compiler notwendigerweise jedes Mal einen neuen Stack-Frame, wenn Sie einen Code-Block eingeben, und kehrt den Stack-Zeiger am Ende des Blocks um?

0 Stimmen

@Alnitak: meine Vermutung wäre, dass es von den Optimierungsstufen abhängt - denken Sie daran, dass der Compiler Ihren Code sogar inline schalten und den Funktionsaufruf ganz weglassen könnte!

0 Stimmen

Ich dachte eher an Blöcke innerhalb von Funktionen, nicht an Funktionsaufrufe.

5voto

aib Punkte 42913

Es sollte beachtet werden, dass C kein Konzept von Stack oder Heap hat, obwohl die vorangegangenen Antworten in ~99% der Fälle richtig sind und einen guten Einblick geben.

C definiert drei Speicherdauern für Objekte: statisch , automatisch y zugewiesen . (§6.2.4.1)

Statische Objekte (z. B. globale Variablen) sind für die Dauer eines gesamten Programms verfügbar.

Automatische Objekte existieren so lange, wie ihre Variable im Geltungsbereich ist. Sie hören auf zu existieren, sobald sie den Geltungsbereich verlassen.

Beachten Sie, dass dies die beiden Extremwerte sind. C gibt Ihnen einen Punkt dazwischen: Zuteilung Objekte. (Der Suchbegriff würde lauten dynamisch zugewiesener Speicher .) Mit denen, Sie teilen dem Computer mit, wann Objekte ihre Existenz beginnen und beenden sollen. Dies geschieht mit Hilfe der Standardfunktionen malloc() (oder Derivate) und free() .

Streng genommen, müssen Sie nicht müssen aufrufen free() . Oder vielleicht doch (dazu müssten Sie den Standard lesen), aber Sie könnten das alles am Ende von main() kurz vor der Beendigung des Programms. Oder Sie überlassen es dem Betriebssystem, dies für Sie zu tun (was die meisten, wenn nicht alle, tun). Aber das wäre wieder ein Extremfall - Objekte entstehen, wenn Sie malloc() erlöschen, wenn Ihr Programm beendet wird.

Auf die praktischen Auswirkungen brauche ich hier nicht näher einzugehen: Der Speicher ist endlich. Sie können nur eine bestimmte Anzahl von Bytes zuweisen, bevor Ihnen der Speicher ausgeht. Verwendung von statisch Objekte für alles zu verwenden, wäre zu verschwenderisch; der Versuch, Teile oder einen großen Brocken von statisch Speicher wäre schwierig und in jedem Fall mit dem Ansatz der dynamischen Zuweisung vergleichbar. Verwendung von automatisch Die Speicherung von langlebigen Objekten würde Sie dazu zwingen, ihren Geltungsbereich so groß wie möglich zu machen, was in etwa dem Geltungsbereich von statisch Objekte sowieso.

--

Nun zu einigen Anmerkungen:

{
    int *temp = malloc(sizeof(int));
    *temp = 5;
    //free(temp);
}

Beachten Sie, dass temp hier ist ein automatisches Objekt. Es lebt nur so lange wie sein Geltungsbereich, der bei } endet. Das Objekt es zeigt auf wird jedoch zugewiesen. Sie existiert so lange, bis Sie free() für ihre Adresse aufrufen. Da temp die einzige Kopie dieser Adresse enthält, verlieren Sie die Möglichkeit, free() einmal aufzurufen temp aus dem Rahmen fällt. Ein Teil des Speichers wird dauerhaft zugewiesen, ist aber nicht verfügbar. Dies wird als Speicherleck .

Müllabfuhr ist eine weitere Methode zur Verwaltung von Objektspeichern. Eine Implementierung in C könnte wie folgt aussehen:

{
    int *temp = gc_malloc(sizeof(int));
    *temp = 5;
}

Bei der } würde der Computer entscheiden, dass die letzte Referenz, temp dass das zugewiesene Objekt verloren gegangen ist und dass es eine gute Idee wäre, es freizugeben.

Es ist ein Kompromiss, bei dem man sich nicht um free()ing-Objekte kümmern muss (was keine Kleinigkeit ist, wie einfache Beispiele vermuten lassen), aber gc_malloc() ist hier komplexer als ein einfaches malloc(), und es gibt unsichtbaren Code, der an dieser Stelle ausgeführt wird } wo temp aus dem Rahmen fällt. Und es ist ein ganz anderes Thema, wie der Computer entscheiden könnte, dass temp war die letzte Referenz. (Einige praktische Lösungen könnten beinhalten, dass Sie mehr Code um "int *temp" herum schreiben).

3voto

spoulson Punkte 20898

Alnitak hat Recht. Ich möchte darauf hinweisen, was "in der Stapel "wirklich bedeutet.

Wenn ein Programm eine Funktion aufruft, erwartet es, dass die Funktion einen Teil der Arbeit erledigt, dann zurückkehrt und mit der nächsten Codezeile fortfährt. Die Funktion hat keine Möglichkeit zu wissen, wohin sie zurückkehren soll, wenn die Funktion abgeschlossen ist. Daher verfügt die Maschine über einen Aufrufstapel für jedes Programm, der dazu dient, die Adresse der folgenden Anweisung in das Programm zu schieben, bevor die Funktion aufgerufen wird. Die "Return"-Anweisung gibt einfach die Programmadresse aus und springt zu ihr.

Der Stapel ist auch ein praktischer Notizblock für vorübergehenden Platz. Es ist möglich, in unbenutzte Bereiche des Stacks zu schreiben. Das Deklarieren einer lokalen Variablen innerhalb einer C-Funktion bewirkt genau dies. Wenn die Funktion zurückkehrt, muss der Stack nicht aufgeräumt, freigegeben oder anderweitig bearbeitet werden, da es sich ohnehin nur um einen temporären Speicherplatz handelte, der nun nicht mehr benötigt wird.

Im Gegensatz dazu ist der Aufruf malloc() weist Speicher aus dem Heap zu, der explizit für das Programm reserviert wird und so lange im Geltungsbereich bleibt, wie das Programm läuft. Wenn Sie also nicht free() den Speicher, bleibt er zugewiesen und wird als Speicherleck betrachtet.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X