Dynamische Speicherverwaltung INE2 M. Thaler, tham@zhaw.ch Office TG208 http://www.zhaw.ch/~tham 1
Um was geht es? Bisjetzt Beispiel Ranglistenprogramm für Sportveranstaltungen Besser - genaue Anzahl Teilnehmer nicht bekannt - gemäss Auftraggeber zwischen ca. 10 und ca. 100'000 - Speicherung der Daten in einem Array Arraygrösse muss zur Compilationszeit bekannt sein Wahl 100'000 - ist das sinnvoll? Speicherplatz für Array belegen, bzw. anfordern wenn Bedarf bekannt ist zur Laufzeit Frage woher kommt der Speicherplatz? 2
Lernziele Siekönnen die Speicherorganisation von Programmen erklären erklären wie Speicher zur Laufzeit (dynamisch) alloziert wird die wichtigsten Funktionen für dynamische Speicherallokation aufzählen und erklären dynamische Speicherallokation für verschiedenste Datentypen anwenden die Speichergrösse dynamisch anpassen die zwei Methoden zur dynamischen Allokation von 2d Arrays erklären und anwenden 3
Inhalt Speicherorganisation von Programmen Dynamische Allokation von Speicher 4
Leseempfehlung Kapitel 15 Verkettete Listen Wikibooks: C Programmierung https://de.wikibooks.org/wiki/c-programmierung Kapitel 14 Dynamische Speicherverwaltung http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/ 5
slide Speicherorganisation Speicherorganisation aus Sicht"Programm" Code - der Programmcode Daten Stack Heap - globale und statische Variablen - lokale Daten (Variablen in Funktionen) automatische Variablen - Parameter für Funktionen - dynamische Allokation von Speicher Code Daten Heap Stack 6
Speicherorganisation Daten: globale und statische Variablen int i; void foo(int a) { static int j; } Stack: lokale Variablen und Parameter beim Funktionsaufruf automatisch erzeugt (auf Stack) beim Rückkehr wieder abgebaut void foo(int a) { int j; } Bei Verlassen einer Funktion gehen die Daten auf dem Stack verloren 7
Speicherorganisation Achtung beware of int *foo(int a) { int j; j = ; return &j; } int *foo(int a) { int j; a = ; return &a; } 8
Dynamischer Speicher in C Dynamische Allokation von Speicher in C Speicher muss - explizit alloziert bzw. angefordert werden - explizit freigegeben werden, falls nicht Memory Leak Speicher wird auf Heap alloziert zwei grundlegende Funktionen - malloc(): alloziert Speicher - free(): gibt Speicher frei zwei weitere Funktionen - calloc(): alloziert Speicher und setzt ihn auf 0 - realloc(): vergrössert oder verkleinert Speicher 9
Speicher allozieren Funktion void *malloc(size_t size) alloziert Speicherplatz der Grösse size in Bytes gibt Pointer auf den Anfang des Speicherbereichs zurück Anwendung muss in den richtigen Datentyp konvertiert werden für Berechnung der Grösse sizeof(datentyp) verwenden Rückgabewert sollte auf NULL überprüft werden (Fehler) Beispiel - Speicherbereich für 100 Integer allozieren int *iarray = (int *)malloc(100 * sizeof(int)); 10
slides Speicher freigeben Funktion void free(void *ptr) gibt dynamisch allozierten Speicherplatz frei ptr zeigt nun auf ungültigen Speicherbereich Anwendung Vermeidung von Fehlern: Pointer sollte auf NULL gesetzt werden free(iptr); Achtung: bei Benutzung von iptr nun Laufzeitfehler Achtung iptr = NULL; unbenutzten Speicher freigeben, sonst Memory Leak 11
Beispiel Einfache Variablen jede einfache Variable dynamisch allozierbar sinnvoll? int *pi = (int *)malloc(sizeof(int)); double *pd = (double *)malloc(sizeof(double)); *pi = 256; *double = 0.48; free(pi); free(pd); 12
Beispiel Dynamischer Array von Integern int *iarray; int n = 0; printf("array Laenge: "); scanf("%d", &n); iarray = (int *)malloc(n * sizeof(int)); /* Zugriff mit "Klammernotation" */ iarray[95] = 12; /* Zugriff mit Zeigernotation */ printf("%d\n", *(iarray + 95)); free(iarray); 13
Beispiel Structs auch Structs bzw. Array von Structs können zur Laufzeit (dynamisch) alloziert werden typedef struct { int h, min, sec; } Zeit; Struct 14 Zeit *pzeit; pzeit = (Zeit *)malloc(sizeof(zeit)); pzeit->h = 4; free(pzeit); Zeit *pzeit; pzeit = (Zeit *)malloc(10 * sizeof(zeit)); pzeit[5]->h = 4; free(pzeit); Array von Structs
Beispiel Strings Einlesen Länge des Strings unbekannt - Buffer genügend gross wählen Speichern und Umkopieren - strlen() liefert Stringlänge ohne '\0' - strcpy() kopiert '\0' strlen()+1 Zeichen char *buffer = (char *)malloc(256); scanf("%s", buffer); char *string = (char *)malloc(strlen(buffer)+1); strcpy(string, buffer); free(buffer); free(string); 15
Speicher allozieren und mit 0 initialisieren Funktion void *calloc(size_t nmemb, size_t size) alloziert nmeb Datenelemente der Grösse size setzt die Datenelemente auf 0 gibt Pointer auf den Anfang des Speicherbereichs zurück Anwendung muss in den richtigen Datentyp konvertiert werden Rückgabewert sollte auf NULL überprüft werden (Fehler) Beispiel - Speicherbereich für 100 Integer allozieren und initialisieren int *iar = (int *)calloc(100, sizeof(int)); 16
Speichergrösse anpassen Funktion void *realloc(void *ptr, size_t size) vergrössert / verkleinert bereits allozierten Speicher "ptr" neue Grösse size bestehender Speicherinhalt wird übernommen bzw. "gekürzt" wichtig: erzeugt neuen Pointer Anwendung Beispiel: verdoppelt Speicherbereich int *iarray; iarray = (int *)malloc(100*sizeof(int)); iarray = (int *)realloc(null, 100*sizeof(int)); 17 iarray = (int *)realloc(iarray, 200*sizeof(int));
2d-Arrays Zwei Varianten 1-d Array verwenden - 2-d Array 1-d Array mit Länge rows*cols - Zugriff: index selber bestimmen array[i*cols + j] Array von Zeigern auf Array von Elementen - Zugriff mit array[i][j] - Array mit Typ type **array - Achtung nicht kompatibel mit Typ type array[10][10] Achtung Varianten nicht über Casts mischbar Laufzeitfehler falls notwendig umkopieren 18
2d-Arrays Variante 1: Modellierung mit 1d-Array int *iarray; int rows, cols; printf("anzahl rows und cols: "); scanf("%d %d", &rows, &cols); iarray = (int *)malloc(rows * cols * sizeof(int)); if (iarray == NULL) 19 for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { iarray[i*cols + j] = i + j; } } free((void *)iarray);
2d-Arrays Variante2: Array von Zeiger int **parray; int rows, cols; printf("anzahl rows und cols: "); scanf("%d %d", &rows, &cols); parray = (int **)malloc(rows * sizeof(int*)); for (int i = 0; i < rows; i++) parray[i] = (int *)malloc(cols * sizeof(int)); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) parray[i][j] = i * j; } free() for (int i = 0; i < rows; i++) free((void *)parray[i]); free((void **)parray); 20
Typische Fehler Speicher zu früh freigeben Speicher wird bei erneuter Anforderung wiederverwendet Zugriff über solchen Pointer "dangling pointer" Fehler Ratschlag: Prointer nach free() auf NULL setzen Falsche Grösse free(ptr); ptr = NULL; n = 90; int *ptr = (int *)malloc(n*sizeof(int)); ptr[95] = 12; Datum *dat = (Datum *)malloc(sizeof(datum*)); 21
typische Fehler Pointer überschreiben ohne Freigabe int array[100]; n = 90; int *ptr = (int *)malloc(n*sizeof(n)); ptr = &array[0]; Verändern eines"allozierten" Pointers int *ptr = (int *)malloc(n*sizeof(n)); ptr = ptr + 1; free(ptr); 22