14.8 Speicher dynamisch reservieren mit »realloc()« und »calloc()« 

In der Headerdatei <stdlib.h> sind noch zwei weitere Funktionen zum dynamischen Reservieren von Speicher deklariert. Hier sehen Sie die Syntax zu diesen Funktionen:
void *calloc(size_t anzahl, size_t groesse); void *realloc(void *zgr, size_t neuegroesse);
Die Funktion calloc() ist der Funktion malloc() sehr ähnlich, nur dass es bei der Funktion calloc() nicht einen, sondern zwei Parameter gibt. Im Gegensatz zu malloc() können Sie mit calloc() noch die anzahl von Speicherobjekten angeben, die reserviert werden soll. Wird z. B. für 100 Objekte vom Typ int Speicherplatz benötigt, so erledigen Sie dies mit calloc() folgendermaßen:
int *zahlen; zahlen = calloc(100,sizeof(int));
Außerdem werden mit der Funktion calloc() alle Werte des allozierten Speicherbereichs automatisch mit dem Wert 0 initialisiert. Bei malloc() hat der reservierte Speicherplatz zu Beginn einen undefinierten Wert. Allerdings können Gleitpunkt- und Zeiger-Nullen auch ganz anders dargestellt werden, weshalb man sich auf solchen Feldern nicht auf die Nullen verlassen kann. Gleichwertig zu calloc() verhält sich außerdem folgendes Code-Konstrukt mit malloc():
ptr = calloc(100, sizeof(int)); // Alternative dafür mit malloc(); erfüllt denselben Zweck ptr = malloc(100 * sizeof(int)); memset(ptr, 0, 100 * sizeof(int));
Da calloc() außer den beiden eben genannten Unterschieden genauso funktioniert wie die Funktion malloc(), gehe ich nicht mehr näher darauf ein.
Interessanter ist dagegen die dynamische Speicherreservierung mit der Funktion realloc(). Mit dieser Funktion ist es möglich, während des laufenden Programms so viel Speicher zu reservieren, wie Sie benötigen. Des Weiteren können Sie sich darauf verlassen, dass ein neuer Pool mit malloc() erstellt wird und die ganzen Ergebnisse herüberkopiert werden, wenn im aktuellen Speicherblock nicht mehr genügend freier Speicher vorhanden ist.
Mit realloc() ist es noch einfacher, z. B. dynamische Arrays zu programmieren. Die Anfangsadresse des dynamischen Arrays ist diejenige, auf die der Zeiger (zgr) zeigt. Der Parameter neuegroesse dient dazu, einen bereits zuvor allozierten Speicherplatz auf neuegroesse Bytes zu vergrößern. Die Funktion realloc() ermöglicht es auch, den Speicherplatz zu verkleinern. Dazu wird einfach der hintere Teil des Speicherblocks freigegeben, während der vordere Teil unverändert bleibt. Bei einer Vergrößerung des Speicherplatzes mit realloc() behält der vordere Teil auf jeden Fall seinen Wert, und der neue Teil wird einfach hinten angehängt. Dieser angehängte Wert ist aber wie bei malloc() undefiniert. Hier sehen Sie ein kleines Beispiel dafür, wie ein Array mit der Funktion realloc() dynamisch erstellt wird:
/* realloc1.c */ #include <stdio.h> #include <stdlib.h> int main(void) { int n=0, max=10, z,i; int *zahlen=NULL; /* Wir reservieren Speicher für 10 int-Werte mit calloc. */ zahlen = calloc(max, sizeof(int)); if(NULL == zahlen) { printf("Kein virtueller RAM mehr vorhanden ... !"); return EXIT_FAILURE; } printf("Zahlen eingeben --- Beenden mit 0\n"); /* Endlossschleife */ while(1) { printf("Zahl (%d) eingeben : ", n+1); scanf("%d", &z); if(z==0) break; /* Reservierung von Speicher während der Laufzeit * des Programms mit realloc */ if(n >= max) { max += max; zahlen = realloc(zahlen,max*sizeof(int)); if(NULL == zahlen) { printf("Kein virtueller RAM mehr vorhanden ... !"); return EXIT_FAILURE; } printf("Speicherplatz reserviert " " (%d Bytes)\n", sizeof(int) * max); } zahlen[n++] = z; } printf("Folgende Zahlen wurden eingegeben ->\n\n"); for(i = 0; i < n; i++) printf("%d ", zahlen[i]); printf("\n"); free(zahlen); return EXIT_SUCCESS; }
Den benötigten Speicherbedarf könnten Sie in diesem Beispiel auch einzeln allozieren. Die einfache Anwendung dieser Funktion soll nicht darüber hinwegtäuschen, dass auch hier erst der alte Speicherbereich temporär zwischengespeichert werden muss, so wie bei der Funktion malloc(). In diesem Fall ist es aber einfacher, da Sie sich nicht mehr selbst darum kümmern müssen.
Im Beispiel wurde der Speicherplatz nach jedem erneuten Allozieren mit calloc() gleich verdoppelt (max += max). Dies ist nicht optimal. Benötigt ein Programm z. B. täglich 500 double-Werte, wäre es am sinnvollsten, erst nach 500 double-Werten neuen Speicher zu allozieren. Somit müsste das Programm nur einmal am Tag neuen Speicher bereitstellen.
Dasselbe Beispiel lässt sich recht ähnlich und einfach auch auf char-Arrays umschreiben. Das folgende Listing demonstriert die dynamische Erweiterung eines Strings:
/* dyn_string1.c */ #include <stdio.h> #include <string.h> #include <stdlib.h> #define BUF 255 int main(void) { size_t len; char *str = NULL; char puffer[BUF]; printf("Ein dynamisches char-Array für Strings\n"); printf("Eingabe machen : "); fgets(puffer, BUF, stdin); str = malloc(strlen(puffer)+1); if(NULL == str) { printf("Kein virtueller RAM mehr vorhanden ... !"); return EXIT_FAILURE; } strcpy(str, puffer); printf("Weitere Eingabe oder beenden mit \"END\"\n>"); /* Endlossschleife */ while(1) { fgets(puffer, BUF, stdin); /* Abbruchbedingung */ if(strcmp(puffer,"end\n")==0 || strcmp(puffer,"END\n")==0) break; /* aktuelle Länge von str zählen für realloc */ len = strlen(str); /* neuen Speicher für str anfordern */ str = realloc(str,strlen(puffer)+len+1); if(NULL == str) { printf("Kein virtueller RAM mehr vorhanden ... !"); return EXIT_FAILURE; } /* Hinten anhängen */ strcat(str, puffer); } printf("Ihre Eingabe lautete: \n"); printf("%s", str); free(str); return EXIT_SUCCESS; }
Beim char-Array läuft es so ähnlich ab wie schon im Beispiel mit den int-Werten zuvor. Sie müssen allerdings immer darauf achten, dass bei erneuter Speicheranforderung mit realloc() das Stringende-Zeichen berücksichtigt wird (+1). Ansonsten ist der Vorgang recht simpel: String einlesen, Zeichen zählen, erneut Speicher reservieren und hinten anhängen.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.