User Tools

Site Tools


dydaktyka:cprog:2016:dynamic_memory_allocation

This is an old revision of the document!


Dynamiczna alokacja pamięci

W języku C można wyróżnić dwie podstawowe metody alokacji (przydzielania) pamięci:

  • alokacja statyczna – ilość pamięci zostaje z góry określona na etapie pisania programu (poprzez odpowiednie deklaracje zmiennych); pamięć jest zwalniana automatycznie – po zakończeniu bloku programu, w którym była deklarowana (chyba, że obiekt został zadeklarowany jako static), lub po zakończeniu programu
    int a;
    char str[] = "Hello!";
    float tab[5];
  • alokacja dynamiczna – program przydziela dowolne ilości pamięci w trakcie pracy, poprzez wywołanie odpowiednich funkcji; pamięć musi być jawnie zwolniona (z użyciem odpowiedniej funkcji)
    int* tab;
    tab = malloc(5 * sizeof(*tab));
    free(tab);

Dynamiczna alokacja pamięci pozwala przede wszystkim na:

  • utworzenie tablicy o rozmiarze obliczanym dopiero podczas działania programu
  • dostęp do zmiennych utworzonych wewnątrz funkcji po wyjściu z nich

Przydzielanie pamięci – funkcja "malloc"

Funkcja malloc() (od memory allocation) rezerwuje blok wolnej pamięci o zadanym rozmiarze (w bajtach) i zwraca adres jego pierwszego bajtu. W przypadku nieznalezienia wymaganego obszaru malloc() zwraca wskaźnik zerowy (dla przypomnienia – wskaźnik o wartości 0).

Ponieważ rezerwowany blok może być zmienną dowolnego typu, funkcja ta zwraca uniwersalny “wskaźnik na void”. Przypisanie takiego wskaźnika do wskaźnika dowolnego innego typu jest operacją dozwoloną przez standard języka.

Aby utworzyć tablicę typu double o zadanym rozmiarze z użyciem funkcji malloc() można napisać następujący kod:

int rozmiar = 10;
double* tab;
tab = malloc(rozmiar * sizeof(*tab));

Powyższy kod zarezerwuje obszar pamięci dla 10 wartości typu wskazywanego przez tab (czyli double) i przypisze adres wskaźnikowi tab. Zauważ, że zmienna tab została zadeklarowana jako wskaźnik do pojedynczej wartości double, a nie do bloku 10 takich wartości. Dlaczego? Ponieważ nazwa tablicy jest zarazem adresem jej pierwszego elementu, powyższe przypisanie umożliwia korzystanie z tab jak ze zwykłej nazwy tablicy (np. tab[0] to wartość jej pierwszego elementu).

Korzystanie z tablic dynamicznych

Z tablic utworzonych w sposób dynamiczny korzysta się identycznie jak z tablic utworzonych statycznie:

int rozmiar = 10;
double* tab = malloc(rozmiar * sizeof(*tab));
tab[3] = 4;
printf("%d\n", tab[3]);

Zwalnianie pamięci – funkcja "free"

Funkcja free() zwalnia dynamicznie przydzieloną pamięć (i tylko taką – próba zwalniania pamięci przydzielonej statycznie to błąd). Argumentem funkcji free() jest adres obszaru pamięci do zwolnienia.

Na przykład:

double* tab;
tab = malloc(8 * sizeof(*tab));
free(tab);

:!: System operacyjny wyznacz limit pamięci, jaki może wykorzystać dany program – jego przekroczenie powoduje “ubicie” programu. Zapominając zwolnić przydzieloną pamięć można łatwo doprowadzić do wycieku pamięci i w konsekwencji właśnie do przedwczesnego zakończenia programu…

Zmiana rozmiaru bloku pamięci – funkcja "realloc"

Funkcja realloc() służy do zmiany rozmiaru zaalokowanego dynamicznie bloku pamięci. Jej pierwszym argumentem jest adres bloku pamięci, a drugim – nowy rozmiar (w bajtach).

Do zwalniania pamięci przydzielonej przez realloc() służy również funkcja free().

Przykład:

double* tab;
int rozmiar_1 = 10;
int rozmiar_2 = 20;
 
tab = malloc(rozmiar_1 * sizeof(*tab)); // pierwotnie `tab` ma rozmiar 10 bajtow
tab = realloc(tab, rozmiar_2 * sizeof(*tab)); // teraz `tab` ma rozmiar 20 bajtow
free(tab);

Alokacja pamięci dla tablicy dwuwymiarowej

Aby utworzyć dynamiczną tablicę dwuwymiarową, możemy postąpić w następujący sposób: Najpierw alokujemy pamięć dla “wierszy”, a następnie dla “kolumn”. Podczas zwalniania pamięci postępujemy na odwrót – najpierw zwalniamy pamięć przydzieloną dla “kolumn”, a potem dla “wierszy”.

Przykład:

#include <stdio.h>
#include <stdlib.h>
 
int main ()
{
    int w = 6;  //liczba wierszy
    int k = 3;  // liczba kolumn
    double **tab2d;
    int i;
 
    tab2d = malloc(w * sizeof(double*));  // alokacja pamięci "wierszy"
 
    for(i = 0; i < w; i++) {
        tab2d[i] = malloc(k * sizeof(double));  // alokacja pamięci "kolumn"
    }
 
    for(i = 0; i < w; i++) {
        free(tab2d[i]); // zwolnienie pamieci kolumn"
    }
 
    free(tab2d); //uwolnienie pamieci "wierszy"
    tab2d = NULL;
 
    return 0;
}

Inne sposoby tworzenia dynamicznych tablic 2D przestawione są w artykule How to dynamically allocate a 2D array in C?

Zadania podsumowujące

:!: W poniższych ćwiczeniach każdorazowo zadbaj o poprawne zwolnienie przydzielonej dynamicznie pamięci!

Zadanie DAL1D

(poziom trudności: )

Napisz program który alokuje dynamicznie jednowymiarową tablicę (wybranego typu), wypełnia ją losowymi liczbami, a następnie wypisuje jej elementy na ekran.

Zadanie DAL1DCHG

(poziom trudności: )

Zmodyfikuj program z zadania DAL1D tak, aby po utworzeniu tablicy i zainicjalizowaniu jej elementów program powiększył jej rozmiar dwukrotnie (nowe elementy wypełniając zerami) i ponownie wypisał jej elementy na ekran.

Zadanie DAL2D

(poziom trudności: )

Napisz program który alokuje dynamicznie tablicę przechowującą w wierszach następujące wyrazy:

Oto
tablica
dynamiczna.
dydaktyka/cprog/2016/dynamic_memory_allocation.1484545138.txt.gz · Last modified: 2020/03/25 11:46 (external edit)