9. Dynamische Speicherverwaltung Dynamische Speicherverwaltung ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-1 9. Dynamische Speicherverwaltung Wiederholung: Verwendung von dynamischen Datenobjekten (Variablen, Strukturen, ...) ist nur auf Basis des Zugriffs zu den Datenobjekten über die Zeiger möglich. Eine statische Variable ist eine Variable, die in einem Programm definiert und später durch ihren Bezeichner verwendet wird. Sie wird statisch genannt, weil sie während der ganzen Abarbeitung der Funktion (des Blocks), zu der (dem) sie gehört, existiert; d.h. ihr wird Speicherplatz für ganze Abarbeitungszeit zugeordnet. Demgegenüber kann eine Variable auch dynamisch während der Abarbeitung einer Funktion erzeugt und vernichtet werden (ohne jede Beziehung zur statischen Struktur des Programms). Solche Variablen heissen deshalb dynamische Variablen. ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-2 9. Dynamische Speicherverwaltung Zugriff zu den dynamischen Datenobjekten ist nur über Zeiger (indirekt) möglich: Prinzip: ZEIGER DYN. VARIABLE ADRESSE INHALT Zeiger ist eine Hilfsvariable, die die Adresse des Datenobjektes enthält und über diese Adresse auf das Datenobjekt verweist. Beispiel: pa a &a INHALT ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-3 9. Dynamische Speicherverwaltung Prinzip der Verwendung von dynamischen Objekten ► Entsprechende Zeiger (Zeiger auf entsprechende Datentypen) klassisch (statisch) definieren ► Speicherplatz anfordern (reservieren) – dynamische Objekte erzeugen ► Daten auf dynamische Datenobjekte entsprechend (über die Zeiger) abspeichern ► Daten über Zeiger bearbeiten, Ergebnisse ausgeben ► Dynamische Datenobjekte löschen (vernichten), Speicherplatz freigeben ► Inhalte von Zeigern löschen (wenn nötig) ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-4 9. Dynamische Speicherverwaltung Funktionen für Speicherreservierung und Speicherfreigabe malloc() – reserviert Speicherplatz einer bestimmten Größe char *zeichenfeld; int *za; . . . za = (int*) malloc (sizeof(int)); zeichenfeld = (char*) malloc (20*sizeof(char) + 1); calloc() – reserviert Speicherplaz für ein Feld und initialisiert diesen Bereich char *zeichenfeld; double *zahlen; . . . zeichenfeld = (char*) calloc (20, sizeof(char)); zahlen = (double*) calloc (10, sizeof(double)); ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-5 9. Dynamische Speicherverwaltung realloc() – erweirtert oder verkleinert einen reservierten Speicherplatz (reallocate) zeichenfeld = (char*) realloc (zeichenfeld, 80); zahlen = (double*) realloc (zahlen, 100); free() – gibt den Speicherbereich frei, der mit einer der obigen Funktionen reserviert wurde free (zeichenfeld); free (zahlen); zeichenfeld = zahlen = NULL; ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-6 9. Dynamische Speicherverwaltung Beispiel: /* Programm C14_0.c - Demonstration der dynamischen Speicherverwaltung */ #include <stdio.h> main () { char *zeichenfeld, *e_zeichen, *z1, *z2; /* char zeichenkette[] = " int Definitionen von Zeigern Zeichenkette eingeben: *za, *zb, *zc, i; */ "; /* Speicherplatzresevierungen */ za = (int *) malloc (sizeof (int)); zb = (int *) malloc (sizeof (int)); zc = (int *) malloc (sizeof (int)); e_zeichen = (char *) malloc (sizeof (char)); zeichenfeld = (char *) malloc (20 * sizeof (char) + 1); z2 = (char *) malloc (strlen (zeichenkette) + 1); strcpy (z2, zeichenkette); /* Daten auf dynamische Daten- */ printf ("\n%s", z2); /* objekte abspeichern */ gets (zeichenfeld); ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-7 9. Dynamische Speicherverwaltung printf ("\n Variable a eingeben: "); scanf ("%i", za); printf ("\n Variable b eingeben: "); scanf ("%i", zb); *zc = *za + *zb; /* einfache Datenverarbeitungen */ /* je zwei Zeichen wechseln */ Ausgabe von Ergebnissen */ z1 = zeichenfeld; for (i=0; i<19; i+=2) { *e_zeichen = *z1; *(z1++) = *(z1+1); *(z1++) = *e_zeichen; } printf ("\n /* Ergebnisse: %s , %i \n\n", zeichenfeld, *zc); free (za); free (zb); /* Speicherplatzfreigabe */ free (zc); free (zeichenkette); free (e_zeichen); free (z2); } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-8 9. Dynamische Speicherverwaltung Typische Applikationen a) Lineare verkettete Listen ► einfach verkettete L ► doppelt verkettete L b) Baumstrukturen ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-9 9. Dynamische Speicherverwaltung Lineare, einfach verkettete Listen Algebraische Repräsentation: L = [ e1, e2, e3, . . . , en-1, en ] Graphische Repräsentation: L e1 e2 e3 en-1 en Listenelemente enthalten ► Datenobjekte (direkter Zugriff – für einfache Datenobjekte) ► Zeiger auf Datenobjekte (indirekter Zugriff – für strukturierte Datenobjekte) ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-10 9. Dynamische Speicherverwaltung Beispiel 1: Fünf isolierte dynamische Datenobjekte z1 z2 15 z3 25 z4 35 z5 45 55 Programm: int *z1, *z2, *z3, *z4, *z5; . . . z1 = (int*) malloc (sizeof(int)); z2 = (int*) malloc (sizeof(int)); . . . z5 = (int*) malloc (sizeof(int)); scanf("%i", z1); scanf("%i", z2); scanf("%i", z3); ... oder scanf ("%i %i %i %i %i", z1, z2, z3, z4, z5); ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-11 9. Dynamische Speicherverwaltung Beispiel 2: Fünf dynamische Datenobjekte in einer einfach verketteten Liste, Werte direkt in Elementen L w1 w2 w3 w4 w5 Programm: struct list_el { int wert; struct list_el *next; } ; main () { struct list_el *L=NULL, *hilf; int i; /* Struktur Listenelement /* nur Schablone /* zwei Zeiger /* Zähler */ */ */ */ ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-12 9. Dynamische Speicherverwaltung for (i=1; i<=5; i++) { /* Erzeugung und Eingabe hilf = (struct list_el *) malloc (sizeof(list_el)); scanf ("%i", &hilf->wert); hilf -> next = L; */ L = hilf; } hilf = L; while (hilf) /* Ausgabe */ { printf (" . . . %i", hilf -> wert); hilf = hilf -> next; } while (L) { hilf = L; /* Speicherfreigabe */ L = L -> next; free (hilf); } } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-13 9. Dynamische Speicherverwaltung Beispiel 3: Fünf dynamische Datenobjekte in einer einfach verketteten Liste, Werte in eigenen Elementen L w1 w2 w3 w4 w5 Programm: typedef struct list_el { /* int *wert; struct list_el *next; } item, *Pitem; main () { Pitem L = NULL, hilf; int i; Struktur Listenelement /* */ Typdefinitionen */ /* */ */ zwei Zeiger /* Zähler ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-14 9. Dynamische Speicherverwaltung for (i=1; i<=5; i++) { /* Erzeugung und Eingabe */ hilf = (Pitem) malloc (sizeof(item)); hilf->wert = (int*) malloc (sizeof(int)); scanf ("%i", hilf->wert); hilf -> next = L; L = hilf; } hilf = L; /* while (hilf) Ausgabe */ Speicherfreigabe */ { printf (" . . . %i", *hilf -> wert); hilf = hilf -> next; } while (L) { /* hilf = L; L = L -> next; free (hilf->wert); free (hilf); } } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-15 9. Dynamische Speicherverwaltung Das erste Programm mit dynamischen Datenobjekten #include <stdio.h> main () { typedef struct element *P_element; typedef struct element { int value; P_element next; } ELEM; P_element L; L = (P_element) malloc (sizeof(ELEM)); L -> value = 5; L -> next = NULL; printf (" L -> value = %d \n\n", L -> value); } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-16 9. Dynamische Speicherverwaltung #include <stdio.h> #define NEW(PP) ((PP) = (PITEM)malloc(sizeof(ITEM))) #define TEST(PP) if (NEW(PP) == NULL) {printf("Memory error\n"); return;} typedef struct item { /* structure item */ /* new types */ int val; struct item *next; } ITEM, *PITEM; main() { int i, n; PITEM current, first, prev; printf ("Enter a positive integer value: "); scanf ("%d", &n); if (n < 1) { printf (" Number must be positive! \n"); return; } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-17 9. Dynamische Speicherverwaltung TEST(first) /* create the list */ current = first; first->val = 1; for (i=2; i<=n; i++) { TEST(current->next) current = current->next; current->val = i; } current->next = NULL; /* remove elements divisible by 3 */ for (prev = current = first; current != NULL; prev = current, current = current->next) if (current->val % 3 == 0) { prev->next = current->next; free((PITEM)current); current = prev; } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-18 9. Dynamische Speicherverwaltung /* display what is left */ for (current = first; current != 0; current = current->next) printf ("\n%d\n", current->val); /* remove the list */ for (prev = current = first; current != NULL; prev = current, current = current->next) free ((PITEM) prev); first = current; } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-19 9. Dynamische Speicherverwaltung #include <stdio.h> #define N 20 struct item { int zahl; char text[N]; struct item *next; }; int anzahl; struct item *P; int main () { char buffer[N], zeichen = '\0'; void menu(), init(), insert(), delete(), read(), ausgabe(), destroy(); printf ("\n\t\t IMPLEMENTIERUNG DER LISTE \n\n"); anzahl = 0; menu (); zeichen = getchar(); getchar(); ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-20 9. Dynamische Speicherverwaltung while (zeichen != 'e') { switch (zeichen) { case 'd': delete(); break; case 'e': destroy(); break; case 'i': init(); break; case 'l': printf ("\n Alle Listenelemente auflisten: \n\n"); ausgabe(); break; case 'n': printf ("\n Gebe den Text für das %d.Element ein: ", anzahl+1); scanf("%s", buffer); insert (buffer); getchar(); break; ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-21 9. Dynamische Speicherverwaltung case 'r': printf ("\n Inhalt des letzten Listenelements ist: "); read (buffer); printf (" %s\n", buffer); break; default: zeichen = '\0'; } if (zeichen == 'e') break; else { menu (); zeichen = getchar(); getchar(); } } printf ("\n Demonstration Ende \n\n"); return 0; } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-22 9. Dynamische Speicherverwaltung void menu () { printf ("\n Gewünschte Funktion wählen:\n\n"); printf (" Initialisierung . . . . . . . . . . . . printf (" Ein neues Element eingeben (insert) . . n \n"); printf (" Element löschen (delete) . . . . . . . . d \n"); printf (" Ausgabe des letzten Elements (read) . . r \n"); printf (" Ganze Liste auflisten . . . . . . . . . l \n"); printf (" Programm beenden . . . . . . . . . . . . e \n"); printf ("\n Das entsprechende Zeichen eingeben: i \n"); "); } void init () { anzahl = 0; P = NULL; printf ("\n Neue Liste \n"); } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-23 9. Dynamische Speicherverwaltung void insert (char *buf) { int i; struct item *Q; Q = malloc (sizeof (struct item)); Q->zahl = ++anzahl; strcpy (Q->text, buf); Q->next = P; P = Q; printf ("\n Element *%s* wurde eingefügt \n", buf); } void delete (void) { struct item *Q; if (P != NULL) { Q = P; P = P->next; printf ("\n Element *%s* wurde gelöscht\n", Q->text); free (Q); anzahl--; } else printf ("\n !!! Leere Liste !!! \n"); } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-24 9. Dynamische Speicherverwaltung void read (char *buf) { int i; strcpy (buf, P->text); } void ausgabe (void) { int i; struct item *Q; Q = P; printf (" Anzahl von Listenlementen: for (i=0; i<anzahl; i++) printf ("\n %d\n", anzahl); { %d.Element = %s\n", Q -> zahl, Q -> text); Q = Q -> next; } } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-25 9. Dynamische Speicherverwaltung void destroy (void) { struct item *Q; while (P != NULL) { Q = P; P = P->next; printf ("\n Element *%s* wurde gelöscht\n", Q->text); free (Q); anzahl--; } } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-26 9. Dynamische Speicherverwaltung Baumstrukturen – mehrfach verzweigte Strukturen Bäume – binäre – n-äre (z.B. Quadtrees) ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-27 9. Dynamische Speicherverwaltung Binäre Suchbäume Kurt Franz Rolf Dieter Erich Hans Leo W Werner Karl ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-28 9. Dynamische Speicherverwaltung Ein neues Element „Sepp“ einfügen: Kurt Franz Rolf Dieter Erich Hans Leo W Werner Karl ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-29 9. Dynamische Speicherverwaltung Ein neues Element „Sepp“ einfügen: Kurt Franz Rolf Dieter Erich Hans Leo Karl W Werner Sepp ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-30 9. Dynamische Speicherverwaltung Das Element „Hans“ löschen: Kurt Franz Rolf Dieter Erich Hans Leo Karl W Werner Sepp ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-31 9. Dynamische Speicherverwaltung Das Element „Hans“ löschen: Kurt Franz Rolf Dieter Erich Karl Leo W Werner Sepp ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-32 9. Dynamische Speicherverwaltung #include <iostream.h> #include <iomanip.h> #include <string.h> struct knoten { knoten *links, *rechts; char *inhalt; } ; // diesmal Zeiger auf String /* Neues Element erzeugen: */ knoten * erzeugeknoten ( const char * name ) { knoten *p; p = new knoten; p->links = p->rechts = NULL; p->inhalt = new char [strlen (name) + 1]; // Speicher fuer String anfordern strcpy (p->inhalt, name); return p; } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-33 9. Dynamische Speicherverwaltung /* Geordnetes Einfuegen eines neuen Elements in den Baum: */ /* Verwendung der Rekursion! */ void suche ( const char *name, knoten * & baum ) { int i; if (baum == NULL) // Ende der Rekursion { baum = erzeugeknoten (name); // Einfuegen des Elements return; } if ( (i=strcmp (name, baum->inhalt) ) < 0 ) suche ( name, baum->links ); // Suche links else if (i>0) suche ( name, baum->rechts ); // Suche rechts else cout << "Name " << name << " ist bereits vorhanden\n"; } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-34 9. Dynamische Speicherverwaltung /* Ausgeben des gesamten Baums mit Anzeige der Tiefe: */ void ausgeben (const knoten * baum, const int tiefe) { if (baum) // != NULL { ausgeben ( baum -> links, tiefe + 1 ); cout << setw(5*tiefe) << " " << baum->inhalt << '\n'; ausgeben ( baum -> rechts, tiefe + 1 ); } } /* Loeschen eines Baumelements: */ void loesche ( const char * name, knoten * & baum ) { int i; knoten * p1; // merkt das zu loeschende Element knoten **p2; // sucht Ersatzelement (call by reference) ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-35 9. Dynamische Speicherverwaltung /* suchen des zu loeschenden Elements */ if (baum == NULL) // Ende der Rekursion { cout << "Element wurde nicht gefunden\n"; return; } if ( (i=strcmp (name, baum->inhalt) ) < 0 ) loesche ( name, baum->links ); else if (i>0) loesche ( name, baum->rechts ); else { // zu ersetzender Knoten ist gefunden p1 = baum; if (p1->rechts == NULL) // ein Nachfolger ist NULL baum = p1->links; else if (p1->links == NULL) // ein Nachfolger ist NULL baum = p1->rechts; else // beide Nachfolger sind nicht NULL { // komplexer Fall, Ersatzelement suchen ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-36 9. Dynamische Speicherverwaltung p2 = &(p1->links); // linker Nachfolger while ( (*p2)->rechts != NULL) // bis zum rechten Ende p2 = &(*p2)->rechts; // jetzt zeigt *p2 auf das Ersatzelement // es folgt die neue Verkettung baum = *p2; *p2 = (*p2)->links;; baum->links = p1->links; baum->rechts = p1->rechts; } /* Element kann geloescht werden: */ delete p1->inhalt; // Speicher fuer String!!! delete p1; // Speicher fuer Element } } int main () { knoten * root = NULL; const int N = 80; char name[N]; ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-37 9. Dynamische Speicherverwaltung cout << "\nGeordnetes Einfuegen und Loeschen in einem Baum\n" << "Namen eingeben. Ende ist leerer String!\n"; cin.getline ( name, N-1 ); while ( strlen(name) != 0 ) { suche ( name, root ); cin.getline ( name, N ); } cout << "\nAusgabe des Baums:\n"; ausgeben (root, 0); cout << "\nWelcher Name soll geloescht werden?\n"; cin.getline (name, N); loesche ( name, root ); cout << "\nAusgabe des neuen Baums:\n"; ausgeben (root, 0); return 0; } ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-38 9. Dynamische Speicherverwaltung Zusammenfassung ► Statische Datenobjekte (Variablen) haben einen Wert, einen Namen und einen Speicherplatz. ► Dynamische Datenobjekte (Variablen) haben nur einen Wert und einen Speicherplatz. ► Zugriff zu den dynamischen Datenobjekten ist nur über Zeiger möglich. ► Zeiger-Variablen (Pointer-Variablen) enthalten immer die Adresse des Datenobjektes, auf das sie verweisen, und sie können statisch oder dynamisch definiert werden. ► Ein dynamisches Datenobjekt erzeugen: Zeiger statisch oder dynamisch definieren Speicherplatz anfordern Daten abspeichern ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-39 9. Dynamische Speicherverwaltung ► Das existierende dynamische Datenobjekt vernichten: Daten bearbeiten und anderswohin abspeichern oder löschen Speicherplatz freigeben Zeiger löschen oder vernichten (wenn dynamisch erzeugt wurde) ► Verkettete Listen – Einfach oder doppelt verkettete lineare Strukturen Zeiger für den Zugriff zur Liste soll (meistens statisch) definiert werden Alle Listenelementen werden dynamisch erzeugt ► Baumstrukturen – Mehrfach verzweigte (nichtlineare) Strukturen Zeiger für den Zugriff zum Baum soll (meistens statisch) definiert werden Alle Baumelementen werden dynamisch erzeugt ► Für beide Typen von dynamischen Datenstrukturen gilt: Ein neues Element mit einem Hilfszeiger erzeugen Alle notwendigen Daten (meistens Zeiger) auf das Element abspeichern Das Element auf entsprechendem Ort der Struktur (Liste, Baum) einfügen ________________________________________________________________________________________________________________________________ V. Matoušek: Informationsverarbeitung / FH Regensburg 9-40