Historia zagadnienia:
Mechanizmy dynamicznego przydzielania pamięci pojawiły się w C wraz z funkcjami standardowej biblioteki malloc/free w <stdlib.h>. Pozwoliły one na realizację struktur o zmiennym rozmiarze, złożonych kolekcji i obiektów, co dało potężny impuls do rozwoju języka i jego zastosowania w dużym programowaniu.
Problem:
Praca z pamięcią dynamiczną wymaga od programisty pełnej kontroli nad cyklem życia obiektów. Błędy (wycieki pamięci, podwójne zwalnianie, niewłaściwe użycie wskaźników) mogą prowadzić do awarii lub podatności (np. do ataków poprzez błąd use-after-free).
Rozwiązanie:
— Zawsze sprawdzaj zwracane wartości malloc/calloc/realloc. Jeśli przydzielenie się nie powiodło — zwracają NULL. — Przy zwalnianiu pamięci ustaw wskaźnik na NULL, aby uniknąć użycia zwolnionego bloku. — Nie używaj wskaźnika po free. — Przestrzegaj poprawnego dopasowania malloc/free oraz calloc/free.
Przykład kodu:
#include <stdio.h> #include <stdlib.h> int main() { int *arr = malloc(5 * sizeof(int)); if (!arr) { perror("Nie udało się przydzielić pamięci"); return 1; } for (int i = 0; i < 5; ++i) arr[i] = i * i; for (int i = 0; i < 5; ++i) printf("%d ", arr[i]); printf(" "); free(arr); arr = NULL; return 0; }
Kluczowe cechy:
Co się stanie, gdy pamięć przydzielona za pomocą malloc zostanie zwolniona za pomocą operatora delete lub odwrotnie (w C++)?
Nie można mieszać mechanizmów przydzielania i zwalniania pamięci między językami (C/C++). W C — tylko malloc/free, w C++ — new/delete.
Co się dzieje, gdy spróbujesz wywołać free(NULL)?
free(NULL) — bezpieczne (jest to gwarantowane przez standard C). Taki wywołanie nic nie robi.
Czy można użyć realloc do zwiększenia lub zmniejszenia bloku pamięci i co się stanie ze źródłowym wskaźnikiem?
realloc może przenieść blok pamięci, a jeśli to się stanie, stary wskaźnik staje się nieprawidłowy. Zawsze przypisuj nowy wskaźnik:
ptr = realloc(ptr, new_size);
Przydzielano pamięć na tablicę w pętli, ale zapomniano zwolnić na końcu iteracji. Przez noc program "zjadł" całą pamięć RAM na serwerze.
Zalety:
Wady:
W pętli pamięć zawsze zwalniano po użyciu, wszystkie kontrole na NULL były wykonywane zaraz po malloc, używano narzędzi debugujących do monitorowania wycieków.
Zalety:
Wady: