B.10 Antworten und Lösungen zu Kapitel 11
-
Es gibt die Funktionen malloc(), calloc() und realloc(). Die einfachste Funktion ist malloc(). Mit ihr wird ein Speicherblock einer bestimmten Größe auf dem Heap reserviert. Die einzelnen Bytes haben dabei am Anfang einen undefinierten Wert. calloc() hingegen initialisiert jedes Byte des reservierten Speicherblocks mit 0. realloc() kann neben der Möglichkeit, wie malloc() einfache Speicherblöcke zu reservieren, zusätzlich den Speicherblock vergrößern, verkleinern oder komplett freigeben.
-
Alle drei Funktionen geben einen typenlosen Zeiger (void*) mit der Anfangsadresse des ersten Bytes des zugeteilten Speicherblocks zurück. Ein Typecasting von void* ist hier nicht nötig, weil dieses vom C-Compiler implizit durchgeführt wird. Allerdings kann man dieses dennoch benutzen, um z. B. die Lesbarkeit des Codes zu verbessern.
-
Nicht mehr benötigten Speicherplatz können Sie mit der Funktion free() wieder freigeben.
-
Der Programmierer hat in diesem Beispiel offensichtlich versucht, in den Zeilen (08) und (09) die Anfangsadressen der beiden dynamischen Arrays zu tauschen. Durch die Zuweisung von iarray1=iarray2 in Zeile (08) ist allerdings der zuvor reservierte Speicherplatz (bzw. die Adresse) von iarray1 für immer verloren. Es wurde hier also ein Memory Leak erzeugt. Am Ende verweisen dann beide Zeiger auf die Adresse von iarray2. Hier hilft dann auch die zweite Zuweisung von iarray2=iarray1 in Zeile (09) nichts mehr. Damit die Adressen der beiden Zeiger in diesem Beispiel tatsächlich getauscht werden können, benötigen Sie einen dritten temporären Zeiger, der die Adresse von iarray1 zwischenspeichert. Im folgenden Codeausschnitt wurde dies durch ein Verfahren behoben, das man Ringtausch nennt:
01 int *iarray1=NULL, *iarray2=NULL;
02 int *tmp=NULL;
03 iarray1 = malloc( BLK * sizeof(int) );
04 iarray2 = malloc( BLK * sizeof(int) );
...
05 for(int i=0; i<BLK; i++) {
06 iarray1[i] = i;
07 iarray2[i] = i+i;
08 }
09 tmp = iarray1; // Ringtausch einleiten
10 iarray1 = iarray2; // zweitem Zeiger erstem zuweisen
11 iarray2 = tmp; // Ringtausch abschließen
... -
Hierzu eine Musterlösung:
00 // kap011/loesung001.c
01 #include <stdio.h>
02 #include <string.h>
03 #include <stdlib.h>
04 #define BUF 4096
05 int main(void) {
06 size_t len;
07 char *str = NULL;
08 char puffer[BUF+1];
09 printf("Text eingeben\n> ");
10 if( fgets(puffer, sizeof(puffer), stdin) == NULL ) {
11 printf("Fehler bei der Eingabe\n");
12 return EXIT_FAILURE;
13 }
14 str = malloc(strlen(puffer)+1);
15 if(NULL == str) {
16 printf("Kein virtueller RAM mehr vorhanden ... !");
17 return EXIT_FAILURE;
18 }
19 strcpy(str, puffer);
20 while(1) {
21 printf("Weiterer Text (Beenden mit ENDE)\n>");
22 if( fgets(puffer, sizeof(puffer), stdin) == NULL ) {
23 printf("Fehler bei der Eingabe\n");
24 return EXIT_FAILURE;
25 }
26 // Abbruchbedingung
27 if( strcmp(puffer,"ende\n")==0 ||
28 strcmp(puffer,"ENDE\n")==0) {
29 break;
30 }
31 // aktuelle Länge von str zählen für realloc
32 len = strlen(str);
33 // neuen Speicher für str anfordern
34 str = realloc(str,strlen(puffer)+len+1);
35 if(NULL == str) {
36 printf("Kein virtueller RAM mehr vorhanden ... !");
37 return EXIT_FAILURE;
38 }
39 // hinten anhängen
40 strcat(str, puffer);
41 }
42 printf("Der gesamte Text lautet (%zd Bytes): \n",
sizeof(str));
43 printf("%s", str);
44 free(str);
45 return EXIT_SUCCESS;
46 } -
Hierzu eine Musterlösung. Die Funktion dazu wurde in den Zeilen (05) bis (11) erstellt. Die main()-Funktion dient nur dazu, die Funktion zu testen.
00 // kap011/loesung002.c
01 #include <stdio.h>
02 #include <string.h>
03 #include <stdlib.h>
04 #define BLK 64
05 void cmp_adress(const int *adr1, const int *adr2 ) {
06 if( adr1 != adr2 ) {
07 printf("realloc musste umkopieren\n");
08 printf("Alte Adresse: %p\n", adr1);
09 printf("Neue Adresse: %p\n", adr2);
10 }
11 }
12 int main(void) {
13 size_t len = BLK;
14 int val = 0;
15 int* ivals = malloc(BLK * (sizeof(int)));
16 if(NULL == ivals) {
17 printf("Kein virtueller RAM mehr vorhanden ...!");
18 return EXIT_FAILURE;
19 }
20 int* iptr = ivals;
21 while(1) {
22 printf("Wie viel neuer Speicher > ");
23 if( scanf("%d", &val) != 1 ) {
24 printf("Fehler bei der Eingabe\n");
25 return EXIT_FAILURE;
26 }
27 // aktuelle Länge von str zählen für realloc
28 len += val;
29 // neuen Speicher für str anfordern
30 ivals = realloc(ivals,len);
31 if(NULL == ivals) {
32 printf("Kein virtueller RAM mehr vorhanden ...!");
33 return EXIT_FAILURE;
34 }
35 cmp_adress( ivals, iptr );
36 iptr = ivals;
37 }
38 return EXIT_SUCCESS;
39 }Das Programm gibt bei der Ausführung Folgendes aus:
Wie viel neuer Speicher > 12345678
realloc musste umkopieren
Alte Adresse: 0000000002551048
Neue Adresse: 0000000000550290
Wie viel neuer Speicher > 2345678
realloc musste umkopieren
Alte Adresse: 0000000003122048
Neue Adresse: 0000000002551048
Wie viel neuer Speicher > 500000
realloc musste umkopieren
Alte Adresse: 0000000003f3f048
Neue Adresse: 0000000003122048
Wie viel neuer Speicher >