- Umožňuje vytvořit datovou za běhu programu
- Instrukce (program; nemění se)
- Statická data (static)
- většina se nemění, jsou přímo v binárce
- globální proměnné (mění se)
- Zásobník (stack)
- mění se pro každou funkci
- lokální proměnné
- Halda (heap)
- mění se při každém
malloc, free
- dynamicky alokované proměnné
- Statická alokace alokuje na zásobníku (stack)
- typicky se uvolňuje po dokončení bloku kódu
- typicky konec funkce, konec cyklu for, …
- výrazně rychlejší
- Na zásobníku nevzniká fragmentace, snadné “uvolněnní” paměťi
- Paměť u vrcholu zásobníku je typicky v cache
- využití pro krátkodobé proměnné / obejkty
int array[10];
- Dynamická alokace na haldě (heap)
- zůstává do explicitního uvolnění (nebo konce aplikace)
- využití pro dlouhodobé paměťové entity
int *array = malloc(variable_len*sizeof(int));
- Specialitou je alokace polí s variabilní délkou (od C99)
- délka není známa v době peřkladu
- ale alokuje se na zásobníku a automaticky odstraňuje
int array[variable_length];
#include <stdlib.h>
void *malloc(size_t n)
- alokuje paměť o zadaném počtu bajtů
- jeden souvislý blok (jako pole)
- na haldě (heap)
- paměť není inicializována (“smetí”)
- pokud se nezdaří, tak vrací
NULL
void *calloc(size_t n, size_t sizItem)
- obdobné jako malloc
- ale alokuje se
n * sizeItem
- + inicializuje paměť na
0
void free(void *)
- uvolní na haldě alokovanou paměť
- musí být ukazatel vrácený předchozím
malloc(), calloc() nebo realloc()
- nelze uvolnit jen část paměti (např. od ukazatele modifikovaného pomocí ukazatelové aritmetiky)
- Pozor na přístup paměti po zavolání
free(paměť)
- paměť již může být přidělena jiné proměnné / programu
- Tzv. use-after-free zranitelnost (exploits)
- Pozor,
free() nemaže obsah paměti
- citlivá data dobré předem smazat (klíče, osobní data)
- Je vhodné nastavit uvolněný ukazatel na
NULL
- Opakované free je korektní, pokud je argument
NULL
- Přístup na adresu je sice nevalidní, ale nepřepisuje používaná data
void *realloc( void *ptr, size_t size );
- pokud je
ptr == NULL, tak stejně jako malloc()
- pokud je
ptr != NULL, tak změní velikost alokovaného paměťového bloku na hodnotu size
- pokud
size == 0, tak v POSIXu stejně jako free()
- Obsah původního paměťového bloku zachován
- pokud je nová velikost větší než stará
- jinak je zkrácen
- Při zvětšení je dodatečná paměť neinicializovaná (stejně jako při malloc())
void *memset(void *ptr, int value, size_t num);
- Nastavení paměť na zadanou hodnotu
- Výrazně rychlejší než cyklus for pro inicializaci
- Pracuje na úrovni bajtů
- časté využití v kombinaci se
sizeof()
- pozor na
sizeof() nad ukazatelem z malloc
int array[100];
memset(array, 0, 100 * sizeof(int));
- Dynamicky alokovaná paměť musí být uvolněna
- dealokace musí být explicitně provedena vývojářem (C nemá Garbage collector)
- Valgrind - nástroj pro detekci memory leaks
valgrind -v --leak-check=full tsetovaný program
- (Na windows Dr. memory - https://www.drmemory.org )
- 2D pole je polje ukazatelů na 1D pole
void allocate2DArray() {
const int LEN = 100;
int **array2D = malloc(LEN * sizeof(int*));
for (int i = 0; i < LEN; i++) {
array2D[i] = malloc((i+1) * 10 * sizeof(int));
}
// ...
for (int i = 0; i < LEN; i++) {
free(array2D[i]); array2D[i] = NULL;
}
free(array2D); array2D = NULL;
}
- struktura s “neomezenou” velikostí
- položky na haldě
- logicky propojené ukazateli
- položky nejsou v poli za sebou
- Při dealokaci nastave proměnnou na
NULL
- opkakovaná dealokace nezpůsobí pád
- validitu ukazatele nezle testovat, ale hodnotu NULL ano
- Dynamicky alokovanou paměť přiřazujeme do ukazatele
sizeof(pointer) vrací velikos ukazatele, nikoli velikost pole!
- Dynamická (de-)alokace nenechává paměť v původním stavu
- neinicializovaná paměť & zapomenuté klíče
- Zobrazit neuzavřené popisovače souborů (fd)
--track-fds=yes
- Pokud chcete sledovat i pod-procesy
--trace-children=yes
- Detekce ppoužití neinicializovaných promněných
--undef-value-errors=yes (default)
- Zjistit odkud neinicializovaná proměnná pochází
--track-origins=yes (zpomalí výrazně běh aplikace)