11.2 Speicherblöcke vergrößern oder verkleinern
Mit der Funktion realloc() können Sie den Speicherplatz eines bereits zugeteilten Blocks vergrößern oder verkleinern. Es folgt die Syntax von realloc():
#include <stdlib.h>
void* realloc( void* ptr, size_t size );
Mit dieser Funktion wird die Größe des durch ptr adressierten Speicherblocks geändert, und es wird ein Zeiger auf die Anfangsadresse des neu veränderten Speicherblocks mit der Größe size Bytes zurückgegeben. Der Inhalt des ursprünglichen Speicherblocks bleibt erhalten, und der neue Speicherblock wird hinten angefügt. Ist das nicht möglich, kopiert realloc() den kompletten Inhalt in einen neuen Speicherblock. Damit ist weiterhin garantiert, dass ein zusammenhängender Speicherbereich reserviert wird.
Speicher um Blöcke erweitern
Wenn realloc() nach einem vorhandenen Speicherblock keinen zusammenhängenden Speicher mehr bekommt, um den Speicher zu vergrößern, muss der komplette vorhandene Speicherblock in einen zusammenhängenden Bereich umkopiert werden. Da das Umkopieren je nach Umfang der Daten ziemlich aufwendig werden kann, sollten Sie es vermeiden, realloc() für einzelne Elemente zu verwenden. Wenn es möglich ist, reservieren Sie mit realloc() immer größere Speicherblöcke für mehrere Elemente. Damit werden die realloc()-Aufrufe im Programm reduziert, was wiederum den Aufwand des Programms verringert und die Performance erhöht.
Wird für ptr ein NULL-Zeiger verwendet, funktioniert die Funktion realloc() wie malloc() und reserviert einen neuen Speicherblock mit size Bytes. Folgende Aufrufe sind somit identisch:
ptr = malloc( 100 * sizeof(int));
ptr = realloc( NULL, 100 * sizeof(int));
Kann realloc() keinen neuen Speicherplatz reservieren, wird auch hier der NULL-Zeiger zurückgegeben. Der ursprüngliche Speicherblock bleibt unverändert erhalten.
Verkleinern Sie den Speicherbereich, indem Sie für size eine kleinere Größe angeben, als der ursprüngliche Speicherblock groß war, wird der hintere Teil des Speicherblocks ab size freigegeben, und der vordere Teil bleibt erhalten.
// Speicher für 100 int-Elemente reserviert
ptr = malloc( 100 * sizeof(int));
...
// Speicher auf 50 int-Element verkleinert
ptr = realloc( ptr, 50 * sizeof(int));
Vergrößern Sie hingegen den Speicherblock, müssen Sie immer die vorhandene Blockgröße des bereits vorhandenen Speicherblocks in Bytes mitrechnen. Folgendes ist beispielsweise falsch:
// Speicher für 100 int-Elemente reserviert
int block = 256;
ptr = malloc( block * sizeof(int) );
// Hier wird kein neuer Speicherplatz reserviert, es wird
// lediglich erneut Speicher für 256 int-Elemente reserviert.
ptr = realloc( ptr, block * sizeof(int) );
Damit der Speicherblock wirklich vergrößert wird, muss die Angabe des zweiten Parameters auch tatsächlich der gesamten Speichergröße des Blocks im Heap entsprechen. Korrekt wäre daher:
int block = 256;
// Speicher für 256 int-Elemente reservieren.
ptr = malloc( block * sizeof(int) );
...
block += block;
// Speicher für insgesamt 512 int-Elemente anfordern.
// Insgesamt wurde der Speicher um 256 int-Elemente vergrößert.
ptr = realloc( ptr, block * sizeof(int) );
Hierzu ein einfaches Beispiel, das realloc() in der Praxis demonstrieren soll:
00 // Kapitel11/realloc_beispiel.c
01 #include <stdio.h>
02 #include <stdlib.h>
03 #define BLKSIZE 8
04 int main(void) {
05 int n=0, max=BLKSIZE, z=0;
06 int* zahlen = calloc(BLKSIZE, sizeof(int));
07 if(NULL == zahlen) {
08 printf("Kein virtueller RAM mehr vorhanden!\n");
09 return EXIT_FAILURE;
10 }
11 printf("Zahlen eingeben --- Beenden mit 0\n");
12 while(1) {
13 printf("Zahl (%d) eingeben: ", n+1);
14 if( scanf("%d", &z) != 1 ) {
15 printf("Fehler bei der Eingabe\n");
16 return EXIT_FAILURE;
17 }
18 if(z==0) { break; } // Schleifenabbruch
19 if(n >= max-1) {
20 max += BLKSIZE;
21 zahlen = realloc(zahlen, max * sizeof(int) );
22 if(NULL == zahlen) {
23 printf("Kein virtueller RAM mehr vorhanden!");
24 return EXIT_FAILURE;
25 }
26 printf("Neuer Speicher: %d Bytes\n", BLKSIZE);
27 printf("Insgesamt : %zd Bytes\n",
sizeof(int)*max);
28 printf("Platz fuer : %d Elemente\n", max);
29 }
30 zahlen[n++] = z;
31 }
32 printf("Folgende Zahlen wurden eingegeben ->\n\n");
33 for(int i = 0; i < n; i++) {
34 printf("%d ", zahlen[i]);
35 }
36 printf("\n");
37 free(zahlen);
38 return EXIT_SUCCESS;
39 }
Zuerst wird in Zeile (06) ein Speicherblock für BLKSIZE int-Elemente mit calloc() angelegt. Sie könnten hier genauso gut malloc() verwenden. Anschließend geben Sie in der while-Schleife (Zeilen (12) bis (31)) BLKSIZE int-Elemente ein. In Zeile (06) haben Sie dafür einen Speicherblock vom Heap reserviert. Mit der Eingabe von 0 können Sie die Schleife abbrechen. Dies wird in Zeile (18) überprüft. In Zeile (19) wird regelmäßig kontrolliert, ob noch Speicherplatz für weitere Elemente vorhanden ist. Ist das nicht der Fall, wird in die entsprechende if-Abfrage verzweigt (Zeilen (20) bis (25)). Zunächst muss dann in Zeile (20) die alte Größe des Speicherblocks zu dem neu zu reservierenden Speicherplatz addiert werden. Danach wird in Zeile (21) der Speicherplatz um BLKSIZE Elemente vom Typ int erweitert, um weitere Elemente einlesen zu können.
Das Programm gibt bei der Ausführung z. B. Folgendes aus:
Zahlen eingeben --- Beenden mit 0
Zahl (1) eingeben: 123
Zahl (2) eingeben: 234
Zahl (3) eingeben: 345
Zahl (4) eingeben: 456
Zahl (5) eingeben: 678
Zahl (6) eingeben: 789
Zahl (7) eingeben: 912
Zahl (8) eingeben: 345
Neuer Speicher: 8 Bytes
Insgesamt : 64 Bytes
Platz für : 16 Elemente
Zahl (9) eingeben: 321
Zahl (10) eingeben: 432
Zahl (11) eingeben: 543
Zahl (12) eingeben: 0
Folgende Zahlen wurden eingegeben ->
123 234 345 456 678 789 912 345 321 432 543