Zarządzanie pamięcią Pamięć: stos i sterta Statyczny i dynamiczny przydział pamięci Funkcje ANSI C do zarządzania pamięcią Przykłady: Dynamiczna tablica jednowymiarowa Dynamiczna tablica dwuwymiarowa 154
Co to jest stos? Stos (ang. stack) to obszar pamięci w przestrzeni adresowej programu wykorzystywany do specyficznych celów. Stos obsługiwany jest w sposób automatyczny, obsługa ta nie wymaga ingerencji programisty. Na stosie dostępny jest wyłącznie element położony na jego wierzchołku, a elementy zdejmowane są ze stosu w odwrotnej kolejności niż były na nim umieszczane (rejestr LIFO). Na stosie przechowywane są: zmienne lokalne; argumenty wywołania funkcji; wartość zwracana przez funkcję; adres wywołania funkcji (miejsce w kodzie); 155
Co to jest sterta? Sterta (ang. heap) to obszar pamięci udostępniany przez system operacyjny wszystkim działającym programom (procesom). Na stercie przechowywane są: dynamicznie tworzone struktury danych; dynamicznie przydzielane obszary pamięci; 156
Statyczny przydział pamięci Rezerwowanie obszaru pamięci w momencie kompilacji kodu źródłowego. ZALETY: łatwa obsługa statycznych obszarów pamięci; prosty zapis w kodzie źródłowym; WADY: konieczność przydzielania pamięci na wyrost w przypadku gdy rozmiar potrzebnej pamięci jest zmienny (nieoszczędne wykorzystanie pamięci); pamięć przydzielona statycznie jest zwalniana dopiero w momencie zakończenia działania programu; 157
Dynamiczny przydział pamięci Rezerwowanie obszaru pamięci w trakcie działania programu. ZALETY: możliwość elastycznego określania zapotrzebowania na pamięć w programie; wykorzystywany jest obszar pamięci o takim rozmiarze, jaki w danym momencie jest wymagany (oszczędne wykorzystanie pamięci); możliwość zwolnienia niewykorzystywanej już pamięci; WADY: niebezpieczeństwo wystąpienia fragmentacji pamięci (używane obszary przeplatają się z nieużywanymi); niebezpieczeństwo wystąpienia efektu przeciekania pamięci (przy braku zwalniania pamięci); 158
Statycznie i dynamicznie const int N = 100; float a[n]; Rozmiar tablicy a został określony w momencie kompilacji kodu źródłowego. float *wsk; int n = 100; wsk = (float *) malloc(n*sizeof(float)); Rozmiar tablicy został określony w momencie działania programu. 159
Funkcje ANSI C do zarządzania pamięcią przydzielanie pamięci (alokacja): malloc() alokacja pamięci z inicjalizacją: calloc() zmiana rozmiaru przydzielonej pamięci (realokacja): realloc() zwalnianie pamięci (dealokacja): free() 160
Funkcje ANSI C do zarządzania pamięcią PRZYDZIELANIE PAMIĘCI (ALOKACJA) void *malloc(size_t size); wskaźnik nieokreślony rozmiar całego obszaru w bajtach PRZYKŁAD: adres zarezerwowanego obszaru pamięci rzutowanie typu int *wsk; wsk = (int *) malloc(10*sizeof(int)); if (wsk == NULL) { czy wskaźnik pusty? printf( Brak pamieci ); exit(1); } KOMÓRKI PAMIĘCI MAJĄ PRZYPADKOWE WARTOŚCI. 161
Funkcje ANSI C do zarządzania pamięcią PRZYDZIELANIE PAMIĘCI Z INICJALIZACJĄ void *calloc(size_t n, size_t size); wskaźnik nieokreślony liczba elementów rozmiar pojedynczego elementu PRZYKŁAD: int *wsk; wsk = (int *) calloc(10, sizeof(int)); KOMÓRKI PAMIĘCI ZOSTAJĄ WYZEROWANE. 162
Funkcje ANSI C do zarządzania pamięcią ZMIANA ROZMIARU PRZYDZIELONEJ PAMIĘCI void *realloc(void *p, size_t size); Zmiana rozmiaru obszaru pamięci już zaalokowanego dynamicznie i wskazywanego przez p na wartość size. Gdy nowy rozmiar jest większy od poprzedniego, wszystkie dane zawarte w obszarze p pozostają niezmienione. PRZYKŁAD: int *wsk; wsk = (int *) malloc(10*sizeof(int)); wsk = (int *) realloc(wsk, 20*sizeof(int)); realloc(wsk, 0); free(wsk); 163
Funkcje ANSI C do zarządzania pamięcią ZWALNIANIE PAMIĘCI (DEALOKACJA) void free(void *p); PRZYKŁAD: int *wsk; wsk = (int *) malloc(10*sizeof(int));... free(wsk); zwolnienie obszaru pamięci wskazywanego przez wsk Nie ma ograniczeń co do kolejności zwalniania obszarów pamięci. NIE WOLNO zwalniać obszarów nie przydzielonych wcześniej funkcjami malloc, calloc, realloc. NIE WOLNO używać obszaru już zwolnionego. Zwalnianie pustego wskaźnika (NULL) nie jest błędem. 164
Dynamiczna tablica jednowymiarowa #include <stdio.h> #include <stdlib.h> float *wsk; int i, n; int main() { printf( Podaj liczbe elementow: ); scanf( %d, &n); wsk = (float *) malloc (n*sizeof(float)); if (wsk == NULL) return -1; for (i = 0; i < n; i++) wsk[i] = i; /* *(wsk+i) = i; */ free(wsk); return 0; } 165
Dynamiczna tablica dwuwymiarowa #include <stdio.h> #include <stdlib.h> float **wsk; int i, j, m, n; int main() { printf( Podaj liczbe wierszy i kolumn: ); scanf( %d %d, &m, &n); wsk = (float **) malloc(m*sizeof(float *)); if (wsk == NULL) return -1; for (i = 0; i < m; i++) wsk[i] = (float *) malloc(n*sizeof(float));... 166
Dynamiczna tablica dwuwymiarowa c.d. }... for (i = 0; i < m; i++) free(wsk[i]); free(wsk); return 0; 167