16.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 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 = (int *)calloc(100,sizeof(int));
Außerdem werden mit der Funktion calloc() alle Werte des alloziierten Speicherbereichs automatisch mit dem Wert 0 initialisiert. Bei malloc() hat der reservierte Speicherplatz zu Beginn einen undefinierten Wert. Allerdings können Gleitkomma- 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(), soll nicht mehr näher auf diese Funktion eingegangen werden.
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, wenn im aktuellen Speicherblock nicht mehr genügend freier Speicher vorhanden ist, dass ein neuer Pool mit malloc() erstellt wird und die ganzen Ergebnisse herüberkopiert werden.
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 alloziierten 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 ein kleines Beispiel, 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 = (int *)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 = (int *)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 alloziieren. 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 Alloziieren 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 alloziieren. 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 = (char *)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 = (char *)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 ähnlich ab wie schon im Beispiel mit den int-Werten zuvor. Es muss allerdings immer darauf geachtet werden, 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.
|