Bisher wurde die Speicherverwaltung in Variablendefinitionen versteckt. Man musste sich so zwar keine Gedanken bezüglich der Verwaltung machen, aber spätestens wenn neuer Speicher für weitere Daten benötigt wurde, musste der Code umgeschrieben werden. C C++ C/C++ dynamisch dynamische Speicherverwaltung Speicher allokieren allozieren malloc calloc realloc alloca Zeiger Arrays Dynamische Speicherverwaltung - Speicher allokieren allozieren malloc calloc realloc alloca Kapitel 17: Dynamische Speicherverwaltung

Bisher wurde die Speicherverwaltung in Variablendefinitionen versteckt. Sie mussten sich so zwar keine Gedanken bezüglich der Verwaltung machen, aber spätestens, wenn Sie neuen Speicher für weitere Daten benötigten, musste der Code umgeschrieben werden.

Ebenso kann es Probleme mit der Gültigkeit einer Variablen geben. Eine Variable existiert nur in dem Anweisungsblock, in dem sie deklariert wurde. Die Gültigkeit des Speicherbereichs dieser Variablen verfällt, sobald dieser Anweisungsblock verlassen wird. Ausnahmen stellen globale und als static deklarierte Variablen dar.

Bei der dynamischen Speicherverwaltung gibt es diese Probleme nicht mehr. Zur dynamischen Speicherverwaltung wird ein Zeiger mithilfe der Funktion malloc() verwendet. Mit dieser Funktion geben Sie an, wie viel Speicherplatz reserviert werden soll. Der Zeiger verweist bei erfolgreicher Reservierung auf die Anfangsadresse des reservierten Speicherblocks. Die Aufgabe des Programmierers ist es, dafür zu sorgen, dass es immer einen Zeiger gibt, der auf diese Anfangsadresse verweist. Der so reservierte Speicher bleibt so lange erhalten, bis dieser entweder explizit mit der Funktion free() freigegeben wird oder das Programm sich beendet.

Es muss dabei natürlich ein gewisser Aufwand betrieben werden, um die dynamische Speicherverwaltung zu realisieren. Wenn dabei unvorsichtig zu Werke gegangen wird, haben Sie schnell eine Zugriffsverletzung mit einem Zeiger verursacht. Ebenfalls zu Problemen kann es kommen, wenn Sie bei einem Programm immer wieder Speicher reservieren und dieser niemals mehr freigegeben wird. Man spricht dabei von Speicherlecks (engl. Memory Leaks). Wie diese und andere Probleme vermieden werden, erfahren Sie in den folgenden Abschnitten.

17.1. Das Speicherkonzept            zurück  Ein Kapitel tiefer  zum Inhaltsverzeichnis

Bevor gezeigt wird, wie Speicher dynamisch reserviert werden kann, folgt ein Exkurs über das Speicherkonzept von laufenden Programmen. Ein Programm besteht aus den vier folgenden Speicherbereichen:
Speicherbereich Verwendung
Code Maschinencode des Programms
Daten Statische und globale Variablen
Stack Funktionsaufrufe und lokale Variablen
Heap Dynamisch reservierter Speicher

Tabelle 17.1: Verschiedene Speicherbereiche in einem Programm

Code-Speicher
Dieser wird in den Arbeitsspeicher geladen, und von dort aus werden die Maschinenbefehle der Reihe nach in den Prozessor (genauer in die Prozessor-Register) geschoben und ausgeführt.

Daten-Speicher Darin befinden sich alle statischen Daten, die bis zum Programmende verfügbar sind (globale und statische Variablen).

Stack-Speicher
Im Stack werden die Funktionsaufrufe mit ihren lokalen Variablen verwaltet. In Kapitel 12, Funktionen, wurde auf den Stack ja schon näher eingegangen.

Heap-Speicher
Dem Heap-Speicher gebührt in diesem Kapitel das Hauptinteresse. Über ihn wird die dynamische Speicherreservierung mit Funktionen wie malloc() erst realisiert. Der Heap funktioniert ähnlich wie der Stack. Bei einer Speicheranforderung erhöht sich der Heap-Speicher, und bei Freigabe wird er wieder verringert. Wenn ein Speicher angefordert wurde, sieht das Betriebssystem nach, ob sich im Heap noch genügend zusammenhängender freier Speicher dieser Größe befindet. Bei Erfolg wird die Anfangsadresse des passenden Speicherblocks zurückgegeben.

17.2. Speicherallokation mit malloc            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Es wurde bereits kurz erwähnt, mit welcher Funktion Speicher dynamisch reserviert werden kann. Es wird dabei auch von einer Speicherallozierung (allocate, dt. zuweisen) gesprochen. Hier die Syntax dieser Funktion:

#include <stdlib.h>

void *malloc(size_t size);

Bei erfolgreichem Aufruf liefert die Funktion malloc() die Anfangsadresse der Größe size Bytes vom Heap zurück. Da die Funktion einen void-Zeiger zurückliefert, ist diese nicht abhängig von einem Datentyp. Hierzu ein Beispiel:

#include <stdio.h>
#include <stdlib.h>

int main()
{
   int *p;

   p=(int *)malloc(sizeof(int));
   if(p != NULL)
      {
         *p=99;
         printf("Allozierung erfolgreich ... \n");
      }
   else
      printf("Kein Speicherplatz vorhanden!!!\n");
   return 0;
}

Nach der Deklaration eines int-Zeigers wurde diesem mit

p=(int *)malloc(sizeof(int));

eine Anfangsadresse eines Speicherbereichs der Größe int zugewiesen. Bei Erfolg zeigt der Zeiger p auf den Anfang des reservierten Speicherbereichs. Ist dabei etwas schief gegangen, zeigt der Zeiger auf NULL, und es wird ausgegeben, dass kein Speicherplatz reserviert werden konnte. Hierzu der Programmablauf bildlich:

Abbildung 17.1: Dynamisch reservierter Speicher vom Heap
Abbildung 17.1: Dynamisch reservierter Speicher vom Heap



Hinweis
 

Das Typencasting der Funktion malloc() ist laut ANSI C-Standard nicht notwendig und kann auch weggelassen werden. ANSI C++ schreibt allerdings ein Casten des Typs void * vor. Da es aber kaum noch reine C-Compiler gibt, wurde und wird in den Beispielen das Typencasting durchgeführt.

Na gut, ich denke, das beeindruckt Sie nicht besonders. Zur Laufzeit eines Programms Speicherplatz für einen int-Wert reservieren mit solch einem Aufwand? Gut, dann reservieren Sie eben mehr Speicherplatz für mehrere int-Werte:

p=(int *)malloc(2*sizeof(int)); 

Hiermit reservieren Sie Speicherplatz für zwei int-Werte vom Heap. Das Beispiel als Listing:

#include <stdio.h>
#include <stdlib.h>

int main()
{
   int *p;
   p=(int *)malloc(2*sizeof(int));

   if(p != NULL)
      {
         *p=99;         /* Alternativ auch p[0] = 99  */
         *(p+1) = 100;  /* Alternativ auch p[1] = 100 */
         printf("Allozierung erfolgreich ... \n");
      }
   else
      printf("Kein Speicherplatz vorhanden!!!\n");

   printf("%d %d\n",p[0],p[1]);
   /* Sie können die Werte auch so ausgeben lassen */
   printf("%d %d\n",*p, *(p+1));
   return 0;
}
Folgende Abbildung soll den Sachverhalt veranschaulichen:

Abbildung 17.2: Speicher für mehrere Elemente dynamisch anfordern
Abbildung 17.2: Speicher für mehrere Elemente dynamisch anfordern

Der Sachverhalt, warum *p und p[0] oder *(p+1) und p[1] auf dasselbe Element zugreifen, wurde in Kapitel 15, Zeiger, geklärt. Blättern Sie notfalls einfach zu den Tabellen am Ende des Kapitels 15 zurück.

17.2.1 NULL-Zeiger bei der Speicherreservierung
Ein NULL-Zeiger wird zurückgeliefert, wenn malloc() nicht mehr genügend zusammenhängenden Speicher finden kann. Der NULL-Zeiger ist ein vordefinierter Zeiger, dessen Wert sich von einem regulären Zeiger unterscheidet. Er wird vorwiegend bei Funktionen zur Anzeige und Überprüfung von Fehlern genutzt, die einen Zeiger als Rückgabewert zurückgeben.

17.2.2 Möglichkeiten der Speicherreservierung und ihre Probleme
Bei einem Aufruf der Funktion malloc() muss die Größe des zu reservierenden Speichers in Bytes angegeben werden. Damit ist die Größe des Speicherobjekts gemeint, das durch einen Zeiger referenziert werden soll. Diese Größe können Sie auch mit numerischen Werten selbst bestimmen:

p=(int *)malloc(sizeof(2)); 

Hiermit werden zwei Bytes reserviert, auf deren Anfangsadresse der Zeiger p verweist. Wird dieser Codeabschnitt jetzt auf ein anderes System portiert, bei dem der Datentyp int einer Größe von vier Bytes entspricht, sind Probleme vorprogrammiert. Daher ist es oft besser, die Größe des Zielobjekts vom Compiler selbst bestimmen zu lassen. Für die dynamische Speicherzuweisung haben Sie folgende drei Möglichkeiten:

#include <stdio.h>
#include <stdlib.h>

int main()
{
   double *p1,*p2;

   p1=(double *)malloc(sizeof(p1));
   p2=(double *)malloc(sizeof(p2));

   if(p1 != NULL && p2 != NULL)
      {
         *p1=5.15;
         printf("p1 = %f\n",*p1);
         *p2=10.99;
         printf("p2 = %f\n",*p2);
      }
   return 0;
}

Wenn Sie "Glück" haben, stürzt das Programm ab. Im schlimmsten Fall funktioniert das Programm und gibt die richtigen Zahlen aus. Das wäre aber purer Zufall. Denn ohne den Dereferenzierungsoperator wird nicht ein double-Wert an malloc() übergeben, sondern die Größe des Zeigers. Und diese beträgt immer vier statt der erforderlichen acht Bytes. Wenn jetzt ein anderer Wert an diese Adresse gelangt, ist der weitere Verlauf des Programms nicht absehbar. Es kommt zu einer so genannten Überlappung der Speicherbereiche.

17.2.3 free() - Speicher wieder freigeben
Wenn Sie Speicher vom Heap angefordert haben, sollten Sie diesen auch wieder zurückgeben. Der alllozierte Speicher wird mit folgender Funktion freigegeben:

#include <stdlib.h>

void free (void *p);

Der Speicher wird übrigens auch ohne einem Aufruf von free() freigegeben, wenn sich das Programm beendet. Ein Beispiel zu free():

#include <stdio.h>
#include <stdlib.h>

int main()
{
   int *p;
   p=(int *)malloc(sizeof(int));

   if(p != NULL)
      {
         *p=99;
         printf("Allozierung erfolgreich ... \n");
      }
   else
      printf("Kein Speicherplatz vorhanden!!!\n");

   if(p != NULL)
      free(p);
   return 0;
}

Es wird hier auch überprüft, dass nur wirklich reservierter Speicherplatz wieder freigegeben wird. Der mit free() freigegebene Speicherplatz wird danach zwar als frei markiert, aber p zeigt immer noch auf die ursprüngliche Speicherstelle. Hier das Beispiel:

#include <stdio.h>
#include <stdlib.h>

int main()
{
   int *p;
   p=(int *)malloc(sizeof(int));

   if(p != NULL)
      {
         *p=99;
         printf("Allozierung erfolgreich ... \n");
      }
   else
      printf("Kein Speicherplatz vorhanden!!!\n");

   printf("vor free() *p = %d\n",*p);
   if(p != NULL)
      free(p);
   printf("nach free() *p = %d\n",*p);
   return 0;
}

Wollen Sie absolut sicher sein, dass der Zeiger nichts mehr zurückgibt, dann übergeben Sie dem Zeiger nach der Freigabe von Speicher einfach den NULL-Zeiger:

free(p);
*p=NULL;

Dies können Sie in ein Makro wie folgt verpacken:

#define free(x)  free(x); *x=NULL 

Was malloc() und die weiteren Speicherallozierungs-Funktionen so bedeutend und wichtig macht, ist die Möglichkeit, von jedem beliebigen Datentyp Speicher anfordern zu können - sind es nun einfache Datentypen wie Strings, Arrays oder komplexe Strukturen.

Natürlich können Sie auch Speicherplatz für ein char-Array zur Laufzeit anfordern. Das folgende Beispiel demonstriert dies:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF 80

int main()
{
   char puffer[BUF];
   char *dyn_string;

   printf("Ein Text mit max. 80 Zeichen: ");
   fgets(puffer, BUF, stdin);

   dyn_string = (char *)malloc(strlen(puffer)+1);
   if(dyn_string != NULL)
      strncpy(dyn_string, puffer, strlen(puffer)+1);
   else
      printf("Konnte keinen Speicherplatz reservieren\n");

   printf("%s",dyn_string);
   free(dyn_string);
   return 0;
}

Wobei erwähnt werden muss, dass diese Art, dynamisch Speicher für einen Text zu reservieren, noch recht unflexibel ist. Mit

dyn_string = (char *)malloc(strlen(puffer)+1); 

wird exakt so viel Speicher angefordert wie zuvor mit fgets() in den String puffer eingelesen wurde. Im Verlauf des Buchs werden Sie erfahren, wie Sie viel effektiver dynamischen Speicher für Text anfordern. Denn bei diesem Beispiel hätten Sie es ja gleich beim char-Array puffer belassen können.

17.3. Die Freispeicherverwaltung            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Ohne mich hier zu sehr in die Details der Freispeicherverwaltung zu verstricken, soll dieses Thema kurz behandelt werden. Als Programmierer kann es Ihnen im Prinzip egal sein, wie ein Betriebssystem seinen Speicher reserviert. Aber wenn Sie irgendwann professionelle Programme schreiben, die häufig Speicher vom Heap anfordern, wäre manches Mal ein wenig Hintergrundwissen wünschenswert.

Außer dem Hauptspeicher und dem Festplattenspeicher gibt es noch weitere Möglichkeiten, Daten zu speichern und zu verarbeiten. Es wird dabei von einer Speicherhierarchie gesprochen, die sich in die folgenden sechs Schichten aufteilt:

Als C-Programmierer bedienen Sie sich allerdings vorwiegend vom Hauptspeicher. Das Betriebssystem verwendet dabei das so genannte Paging zur Verwaltung des Speichers. Als Paging wird die Unterteilung des virtuellen Speichers in Seiten (pages) und des physischen Speichers in Seitenrahmen (page frames) bezeichnet. Die Größe einer Seite beträgt bei den gängigen Betriebssystemen 512 KB oder 1024 KB. Ein virtuelles Speichersystem ist erforderlich, damit auch mehrere Programme laufen können, die nicht alle in den physischen Speicher (echter vorhandener Speicher, RAM) passen würden. Dafür stellt Ihnen das Betriebssystem eine so genannte Adresskonvention zur Verfügung, womit aus einer virtuellen Adresse wieder eine physische Adresse wird. Denn mit virtuellen Adressen allein könnte kein Programm laufen.

Hinweis
 

Jedem Prozess steht ein eigener virtueller Adressraum und darin eine eigene Speicherverwaltung zur Verfügung. Meistens wird hier auch noch das Swapping benutzt. Dabei wird ein Prozess in den Hauptspeicher geladen und läuft eine gewisse Zeit, bis dieser anschließend auf die Festplatte ausgelagert wird. Dieses Swapping findet z.B. statt, wenn nicht mehr genügend Speicherplatz vorhanden ist, um einen Prozess ausführen zu können.

In Abschnitt 17.1 haben Sie bereits etwas über den Heap erfahren. Sie wissen, dass der Heap ein zusammenhängender Speicherplatz im Arbeitsspeicher ist, von dem Sie als Programmierer Speicher allozieren können. Das Betriebssystem verwaltet diesen Speicherbereich als eine Kette von freien Speicherblöcken, welche nach aufsteigenden Speicheradressen sortiert ist. Jeder dieser Blöcke enthält Informationen wie die Gesamtlänge oder den nächsten freien Block. Benötigen Sie jetzt Speicherplatz, durchläuft das Betriebssystem diesen Speicherblock nach verschiedenen Verfahren. Dabei wird von einer prozessinternen Freispeicherverwaltung gesprochen.

17.3.1 Prozessinterne Freispeicherverwaltung
Durch einen Aufruf von malloc() sucht das Betriebssystem jetzt einen zusammenhängenden Speicherblock, welcher den Anforderungen entspricht. Auf der Suche nach diesem Speicherplatz gibt es verschiedene Strategien bzw. Algorithmen, die im Betriebssystem (genauer: im Kernel) implementiert sind.

Als Beispiel dienen freie Speicherbereiche, die folgendermaßen im System angeordnet sind:

Abbildung 17.3: Freie Speicherbereiche im System
Abbildung 17.3: Freie Speicherbereiche im System

Sie fordern jetzt von diesen freien Speicherbereichen mit der Funktion malloc() folgende Speicherblöcke an:

ptr1 = malloc(10);
ptr2 = malloc(12);
ptr3 = malloc(9);

Anhand des freien Speicherbereichs (siehe Abbildung 17.3) und den drei Speicheranforderungen will ich Ihnen die einzelnen Verfahren erklären.

First-Fit-Verfahren
Die Speicherverwaltung durchläuft die Liste der Reihe nach und alloziert den erstbesten freien Bereich, der groß genug ist. Somit sieht die Speicherbelegung im First-Fit-Verfahren folgendermaßen aus:

Abbildung 17.4: First-Fit-Verfahren
Abbildung 17.4: First-Fit-Verfahren

Next-Fit-Verfahren
Next-Fit funktioniert wie First-Fit, nur merkt sich das Next-Fit-Verfahren die aktuelle Position und fährt bei der nächsten Suche nach freiem Speicher von dieser Position aus fort.

Best-Fit-Verfahren
Es wird die gesamte Speicherliste durchsucht, bis ein kleinstmögliches Loch gefunden wird. Mit diesem Verfahren wird eine optimale Speicherausnutzung garantiert.

Abbildung 17.5: Best-Fit-Verfahren
Abbildung 17.5: Best-Fit-Verfahren

Worst-Fit-Verfahren
Das Gegenteil von Best-Fit; es wird in der Liste nach dem größten verfügbaren freien Bereich gesucht, und dieser wird verwendet.

Abbildung 17.6: Worst-Fit-Verfahren
Abbildung 17.6: Worst-Fit-Verfahren

Quick-Fit-Verfahren
Das Quick-Fit-Verfahren unterhält getrennte Listen für freie Bereiche gebräuchlicher Größe.

Buddy-Verfahren
Das Buddy-Verfahren verwendet für jede Speichergröße eine eigene Liste. Die Zeiger auf die Listenköpfe werden dabei in einem Array zusammengefasst. Bei diesem Verfahren werden nur Blöcke von Zweierpotenzen verwendet (1 Byte, 2 Byte, 4 Byte, 8 Byte, 16 Byte, 32 Byte, 64 Byte … 512 Byte, 1 KB … 512 KB, 1 MB …). Wird Speicher angefordert, der nicht diesem Block entspricht, wird ein Block mit der nächsten Zweierpotenz verwendet. Die Blöcke werden außerdem dahin gehend markiert, ob sie zur Anwendung frei sind. Ist bei Speicheranforderung kein gewünschter Block frei, wird ein Block in zwei gleich große Blöcke aufgespalten.

Hinweis
 

Solche Strategien der Freispeicherverwaltung haben natürlich auch ihren Sinn. Vorwiegend dienen solche Verfahren dazu, einen Verschnitt des Speichers zu vermeiden. Das bedeutet, der Speicher wird schlecht ausgenutzt, wenn sehr viele unterschiedliche Stücke verwaltet werden müssen. So kann es passieren, dass einzelne Fragmente des Speichers wahrscheinlich nicht mehr verwendet werden können. Ein zweiter Grund für diese Freispeicherverwaltung ist natürlich die Geschwindigkeit. Je schneller wieder auf einen Speicherblock zurückgegriffen werden kann, umso besser.

Freigabe von Speicher
Der Speicherplatz, der wieder freigegeben wird, wird nicht an das Betriebssystem zurückgegeben, sondern in die Freispeicherliste eingehängt.

Nach diesem Ausflug, der schon mehr in Richtung Programmierung von Betriebssystemen ging, nun wieder zurück zur Praxis.

17.4. Dynamisches Array            zurück  Ein Kapitel höher  zum Inhaltsverzeichnis

Wenn mit der Funktion malloc() ein zusammenhängender Speicherbereich reserviert werden kann, dann muss es auch möglich sein, Speicher für ein Array während der Laufzeit zu reservieren. Bei einem zusammenhängenden Speicher können Sie davon ausgehen, dass dieser in einem Block (lückenlos) zur Verfügung gestellt wird. In dem folgenden Beispiel wird ein solches dynamisches Array erzeugt:

#include <stdio.h>
#include <stdib.h>
#include <string.h>

int main()
{
   int *value;
   int size;
   int i=0;

   printf("Wie viele Werte benötigen Sie : ");
   scanf("%d",&size);
   value=(int *)malloc(size*sizeof(int));
   if( NULL == value )
      {
         printf("Fehler bei malloc....\n");
         exit(0);
      }

   while( i<size )
      {
         printf("Wert für value[%d] eingeben : ",i);
         scanf("%d",&value[i]);
         i++;
      }
   printf("Hier Ihre Werte\n");
   for(i=0; i<size; i++)
      printf("value[%d] = %d\n",i,value[i]);
   return 0;
}

Abbildung 17.7: Dynamisch erzeugtes Array
Abbildung 17.7: Dynamisch erzeugtes Array

Mit

value=(int *)malloc(size*sizeof(int));

wird ein zusammenhängender Speicherbereich mit size int-Werten reserviert. Danach werden mit

while(i<size)
   {
      printf("Wert für value[%d] eingeben : ",i);
      scanf("%d",&value[i]);
      i++;
   }

diesem Speicherbereich Werte zugewiesen. Zum besseren Verständnis dasselbe Programm nochmals, aber statt mit Arrays nun mit Zeigern:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
   int *value;
   int size;
   int i=0;

   printf("Wie viele Werte benötigen Sie : ");
   scanf("%d",&size);

   value=(int *)malloc(size*sizeof(int));
   if(NULL == value)
      {
         printf("Fehler bei malloc...!!\n");
         exit(0);
      }
   while(i<size)
      {
         printf("Wert für value[%d] eingeben : ",i);
         scanf("%d",(value+i));
         i++;
      }
   printf("Hier Ihre Werte\n");
   for(i=0; i<size; i++)
      printf("value[%d] = %d\n",i,*(value+i));
   return 0;
}

Da *value, value[0] und *(value+1), value[1] immer auf dieselbe Speicheradresse verweisen, ist es egal, wie darauf zugegriffen wird.

Das Programm ist jetzt etwas unflexibel. Was ist, wenn Sie für fünf weitere Elemente Speicherplatz benötigen? Mit der Funktion realloc() wäre dies recht einfach zu realisieren. Aber diese steht jetzt noch nicht zur Debatte. Es ist auch mit malloc() möglich, wenn auch etwas umständlicher. Hier das Beispiel:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static int size;
static int merker;

int main()
{
   int *value,*temp;
   int i=0;
   int more;
   printf("Wie viele Werte benötigen Sie : ");
   scanf("%d",&size);
   merker=0;

   value=(int *)malloc(size*sizeof(int));
   if(NULL == value)
      {
         printf("Fehler bei malloc...!! n");
         exit(0);
      }

   do{
         while(merker<size)
            {
               printf("Wert für value[%d] eingeben : ",merker);
               scanf("%d",&value[merker]);
               merker++;
            }
         printf("Neuen Platz reservieren (0=Ende) : ");
         scanf("%d",&more);
         temp=(int *)malloc(size*sizeof(int));
         if(NULL == temp)
            {
              printf("Kann keinen Speicher mehr reservieren!\n");
              exit(0);
            }
         for(i=0; i<size; i++)
            temp[i]=value[i];
         size+=more;
         value=(int *)malloc(size*sizeof(int));
         if(NULL == value)
            {
              printf("Kann keinen Speicher mehr reservieren!\n");
              exit(0);
            }
         for(i=0; i<size; i++)
            value[i]=temp[i];
      }while(more!=0);
   printf("Hier Ihre Werte\n");
   for(i=0; i<size; i++)
      printf("value[%d] = %d\n",i,value[i]);
   return 0;
}

Abbildung 17.8: Dynamisch reserviertes Array dynamisch erweitern
Abbildung 17.8: Dynamisch reserviertes Array dynamisch erweitern

Bevor Sie für das bereits dynamisch reservierte Array erneut Speicherplatz reservieren können, müssen Sie die bereits eingegebenen Werte erst einmal in ein temporär alloziertes Array zwischenspeichern. Danach kann neuer Speicherplatz für das Array reserviert werden, worein anschließend die Werte aus dem temporären Array zurückkopiert werden. Das alles ist ziemlich aufwändig. Ihnen das jetzt anhand eines char-Arrays (Strings) zu demonstrieren, erspare ich mir zunächst.

17.5. Speicher dynamisch reservieren mit realloc und calloc            zum Inhaltsverzeichnis