11.3    Speicherblöcke wieder freigeben

Wenn Sie Speicher vom Heap angefordert haben und nicht mehr benötigen, müssen Sie diesen Speicher wieder an das System zurückgeben. In C muss die Speicherfreigabe dafür explizit mit der Funktion free() durchgeführt werden. Hier die Syntax von free():

#include <stdlib.h>
void free( void* ptr );

Damit geben Sie den dynamisch zugeteilten Speicherblock frei, den Sie mit Funktionen wie malloc(), calloc() oder realloc() angefordert haben und auf dessen Adresse der Zeiger ptr verweist. Ist ptr ein NULL-Zeiger, passiert gar nichts. Da der Speicherbereich mit free() freigegeben wurde, kann er bei späteren Speicheranforderungen wiederverwendet werden.

Bei dem Argument ptr müssen Sie selbst darauf achten, dass auch wirklich ein Zeiger verwendet wird, der zuvor mit einer Funktion wie malloc(), calloc() oder realloc() reserviert wurde. Verwenden Sie einen falschen Zeiger oder wurde der Speicher bereits freigegeben, lässt sich das weitere Verhalten des Programms nicht mehr vorhersagen und ist undefiniert.

Speicher wird beim Programmende automatisch freigegeben

Es wird generell behauptet, dass bei der Beendigung eines Programms das Betriebssystem den reservierten und nicht mehr freigegebenen Speicher selbst organisiert und somit auch wieder freigibt. Meistens ist das auch der Fall, vom Standard wird das aber nicht gefordert. Somit hängt dieses Verhalten von der Implementierung der Speicherverwaltung des Betriebssystems ab.

11.3.1    Memory Leaks (Speicherlecks)

Memory Leaks sind Speicherbereiche, die zwar belegt sind, aber zur Laufzeit weder verwendet noch freigegeben werden können. Ein populäres Beispiel ist die Reservierung von Speicher mittels malloc(). Auf die Anfangsadresse des Speicherblocks verweist ein Zeiger. Geht dieser Zeiger verloren, weil Sie ihn beispielsweise anderweitig verwenden oder bereits erneut einen Speicher mit malloc() reservieren, gibt es keine Möglichkeit mehr, auf diesen Speicherbereich zuzugreifen. Er kann somit auch nicht mehr freigegeben werden. In C gibt es keinen standardisierten Weg der automatischen Speicherbereinigung. Der Speicher muss explizit mit free() freigegeben werden.

In dem nächsten einfachen Beispiel wird ein solches Speicherleck demonstriert:

00  // Kapitel11/memory_leak.c
01 #include <stdio.h>
02 #include <stdlib.h>

03 int main(void) {
04 double *dptr1=NULL, *dptr2=NULL;
05 dptr1 = calloc( 10, sizeof(double) );
06 dptr2 = calloc( 20, sizeof(double) );
07 // dptr1 verweist auf dieselbe Adresse wie dptr2.
08 dptr1 = dptr2; // Speicherleck erstellt
09 //... nach vielen Zeilen Code Speicher freigeben.
10 free( dptr1 ); // Gibt Speicher frei
11 free( dptr2 ); // Fehler!!!
12 return EXIT_SUCCESS;
13 }

In Zeile (05) und (06) wird jeweils ein Speicherblock für 10 bzw. 20 double-Werte reserviert. In Zeile (08) verweisen Sie mit dem Zeiger dptr1 auf dieselbe Adresse wie dptr2. Durch diese Zuweisung haben Sie keine Möglichkeit mehr, um auf den Speicherbereich zuzugreifen, den Sie in Zeile (05) reserviert haben. In einem lange laufenden Programm, wie dies beispielsweise bei Serveranwendungen der Fall ist, könnte der Speicherblock nicht mehr vom Programm freigegeben werden. In diesem Beispiel ist das Speicherleck zwar nicht so tragisch, aber es deckt auf, was passieren kann, wenn man unvorsichtig mit Zeigern umgeht. In Zeile (11) machen Sie außerdem noch einen weiteren Fehler, indem Sie versuchen, denselben Speicherblock freizugeben, den Sie bereits in Zeile (10) freigegeben haben. Das weitere Verhalten des Programms ist dadurch undefiniert.

Das folgende Beispiel enthält ebenfalls ein Speicherleck. In einer Endlosschleife wird mittels malloc() ständig ein Speicherblock angefordert, aber niemals freigegeben. Da immer wieder derselbe Zeiger verwendet wird, kann nach jeder Speicheranforderung nicht mehr auf den zuvor reservierten Speicherbereich zurückgegriffen werden. Damit kann dieser auch nicht mehr freigegeben werden. Der Speicher läuft voll, bis das Programm nicht mehr reagiert:

double *dptr1=NULL;
...
while( 1 ) {
dptr1 = malloc( 10 * (sizeof(double)));
...
}
free(dptr1) // Wird vielleicht nie erreicht

Das Problem in diesem Codeausschnitt können Sie beheben, indem Sie gleich am Ende der while-Schleife den Speicherblock wieder freigeben:

double *dptr1=NULL;
...
while( 1 ) {
dptr1 = malloc( 10 * (sizeof(double)));
...
free(dptr1); // Speicherleck stopfen
}