Dynamická alokace paměti v jazyce C
Dynamická alokace paměti v jazyce C je v informatice souhrn funkcí používaných při programování pro dynamickou alokaci paměti, její správu a uvolňování. Správa paměti v programovacím jazyku C je prováděna programátorem ručně, nikoliv automaticky. Cílem je požádat jádro operačního systému o přidělení souvislého úseku operační paměti pro potřeby běžícího procesu nebo ji naopak operačnímu systému vrátit. Proces může paměť alokovat nebo vracet opakovaně. Po ukončení procesu je veškerá alokovaná paměť vrácena operačnímu systému k dalšímu použití.
Implementace
Dynamická alokace paměti je v programovacím jazyce C realizována sadou funkcí ve standardní knihovně jazyka C. Jedná se o funkce malloc()
, realloc()
, calloc()
a free()
.[1][2][3]
V programovacím jazyce C++ jsou tyto funkce pouze pro zachování zpětné kompatibility s jazykem C, preferované je použití operátoru new (new
a new[]
).[4]
Popis činnosti
V jazyce C lze používat statické, automatické i dynamické přidělování (alokaci) paměti.
Staticky alokované proměnné jsou umístěny do hlavní paměti, velmi často přímo s výkonným kódem programu. Existují po celou dobu běhu programu, ať už jsou programem využity nebo ne. Automaticky alokované proměnné jsou alokovány do zásobníku a vznikají a zanikají z potřeby programu. Pro tyto proměnné platí, že velikost jejich alokace musí být v průběhu kompilování neměnná (s výjimkou C99, která umožňuje pole s variabilní délkou).[5] Pokud potřebná velikost není známá do běhu programu (v případě čtení velikosti z disku nebo od uživatele), potom použití fixní velikosti objektu není na místě.
Statické nebo automatické alokování paměti nemusí být vhodné pro všechny situace. Data z automaticky alokované paměti nepřetrvají přes vícenásobné volání funkce (respektive funkcí), zatímco statická data přetrvávají v paměti, ať už jsou programem využita nebo nevyužita. V mnoha případech tak záleží na programátorovi a jeho umu nakládat s alokací paměti.
Těmto problémům se lze vyhnout při použití dynamicky alokované paměti. Paměť je v tomto případě řízena mnohem příměji (ale pružněji), typicky alokováním z volného místa v paměti (halda). V programovacím jazyce C je knihovní funkce malloc()
používána k alokaci paměti právě v haldě. Program přistupuje k této oblasti skrz ukazatel, který mu navrátí funkce malloc()
. Pokud není potřeba tuto oblast paměti dále udržovat pak je pointer předán funkci free()
, která danou paměť označí jako volnou.
Použití funkce malloc()
může předcházet systémovým voláním a to využitím fast bins.[6]
Některé platformy umožňují volání knihovních funkcí, které dynamicky naalokují paměť za běhu programu v zásobníku C namísto haldy (například alloca()
).[7] Tato paměť je automaticky uvolněna když skončí funkce.
Přehled funkcí
Funkce pro dynamické alokování paměti v jazyce C jsou definovány v knihovně stdlib.h
(pro C++ cstdlib
).[1]
Funkce | Popis |
---|---|
malloc() |
alokuje určený počet bytů |
realloc() |
zvětší nebo zmenší velikost konkrétního bloku v paměti, relokuje pokud je třeba |
calloc() |
alokuje určený počet bytů a nastaví jim hodnotu na 0 |
free() |
uvolní určenou paměť pro další použití |
Rozdíly mezi funkcí calloc() a malloc()
Jsou dva rozdíly mezi těmito funkcemi. První je v počtu argumentů. Funkce malloc()
přebírá pouze jeden argument, tím je velikost paměti k alokaci. Zatímco calloc()
přebírá argumenty dva, počet proměnných k alokaci a velikost jedné proměnné v bytech. Druhým rozdílem je inicializace. Funkce malloc()
neinicializuje blok v paměti. Zatímco funkce calloc()
inicializuje všechny byty alokované paměti na nulu.
Příklady použití
Vytvoření pole deseti integerů je v jazyce C jednoduchá záležitost: int pole[10];
Nicméně velikost alokace paměti tohoto pole bude po kompilaci neměnná. Pokud by toto pole bylo alokováno dynamicky, pak by kód vypadal následovně:
int * pole = malloc(10 * sizeof(int));
Toto vypočítá velikost paměti (v bytech), kterou zabere 10 integerů v paměti. Poté požádá o tento počet bytů malloc()
a přiřadí výsledný ukazatel jménem pole
(díky syntaxi jazyka C, ukazatele a pole mohou být zaměnitelné).
Protože malloc()
nemusí být schopen obsloužit tuto žádost, může vrátit ukazatel s nulovou hodnotou a je třeba tuto událost ošetřit v kódu:
int * pole; if ( NULL == (pole = malloc(10 * sizeof(int))) ) { printf("malloc byl chybny\n"); return(-1); }
Pokud program nadále nepotřebuje toto dynamické pole, může zavolat funkci free() a uvolnit tak paměť.
free(pole);
Paměť vyčleněná funkcí malloc() není inicializovaná a může obsahovat data z dřívějších alokací, je tedy zapotřebí danou oblast vyčistit od dřívějších dat. Naproti tomu funkce calloc() inicializuje a vyčistí oblast dat najednou. Následující příklad alokuje oblast v paměti dost velkou na to, aby se do ní vešlo 10 integerů a všechny byty nastaví hodnotou na 0.
int * pole = calloc(10, sizeof (int));
Typová bezpečnost
Funkce malloc()
vrací obecný ukazatel (void *)
, který ukazuje na oblast v paměti neurčeného datového typu. Přetypování není v jazyce C vyžadováno, ale je naopak vyžadováno v silně typových jazycích jako je jazyk C++. Chybějící typ ukazatele vrácený z funkce malloc()
je některými programátory považován za nebezpečné chování procesu, protože funkce malloc()
alokuje paměť na základě počtu bajtů, nikoli však na základě typu. To je rozdíl proti operátoru new()
v jazyce C++, kde typ ukazatele závisí na operandu.
Ukazatel může být v jazyce C přetypován na specifický typ:
int * ukazatel;
ukazatel = malloc(10 * sizeof(int)); /* bez přetypování v jazyce C */
ukazatel = (int *) malloc(10 * sizeof(int)); /* s přetypováním v jazyce C */
Přetypování v jazyce C++:
ukazatel = reinterpret_cast <int *> (malloc(10 * sizeof(int))); /* s přetypováním v jazyce C++ */
Přetypování má své výhody i nevýhody.
Výhody přetypování
Nevýhody přetypování
- Ve standardu ANSI C je přetypování redundantní
- Použití přetypování může maskovat chybu s chybějící knihovnou
stdlib.h
, kde semalloc()
nachází.[8][10] Pokud tento prototypmalloc()
chybí, standardně kompilátor považuje návratovou hodnotu zmalloc()
jakoint
. Pokud není použito přetypování kompilátor nahlásí chybu při přiřazení. Při použití přetypování se tato chyba neobjeví a zůstane skryta. Na mnohých architekturách (jako LP64 kdelong
a ukazatele bývají 64bitové aint
je 32bitový), tato chyba může vést k destrukci zásobníku. Moderní kompilátory už tuto chybu zachytávají a je méně pravděpodobné, že by k ní došlo bez ohledu na to jestli je přetypování používáno nebo ne.
Známé problémy
Špatné používání dynamického alokování paměti může vést k mnoha chybám. Může se jednat o problémy bezpečnostní i problémy s pády programů.
Nejčastější chyby jsou:
- Selhání alokace paměti – alokace paměti pomocí funkce
malloc()
může selhat a v takovémto případě je navrácen nulový ukazatel. Pokud programátor nezajistí kontrolu alokace, proces skončí kvůli chybě s nulovým ukazatelem (null pointer reference). - Únik paměti – jedná se o problém, kdy programátor vůbec nebo nesprávně volá funkci
free()
. Toto plýtvání může vést k tomu, že paměť bude vyčerpána. - Logické chyby – všechny alokace probíhají ve stejném sledu – alokace pomocí
malloc()
, použití paměti a uvolnění paměti funkcífree()
. Velmi často dochází k chybám s dodržením tohoto postupu jako dvojité volání funkcefree()
, opětovné používání paměti po uvolnění funkcífree()
, vedou k chybě a předčasnému ukončení procesu. Tento typ chyb je velmi těžké odhalit. Například uvolnění paměti není okamžitě obslouženo jádrem OS, a tedy již zrušené ukazatele mohou stále existovat.
Předefinování malloc()
malloc()
Protože malloc()
a jeho alternativy můžou mít velký dopad na výkon programu, není neobvyklé že si programátoři tyto funkce přizpůsobí pro své aplikace. Ačkoliv jazyk C přímo toto neumožňuje, existují cesty jak tohoto dosáhnout. Například s využitím knihoven a dynamického propojování a odkazem na jinou knihovnu. V Unixu V.3 je používán ukazatel na funkce free()
a malloc()
, který aplikace mohou přepsat a odkázat tak na vlastní implementaci.[11]
Limity velikosti alokace
Největší možný blok paměti, který malloc()
může alokovat závisí na operačním systému, fyzické velikosti paměti apod. Teoreticky je největší možná velikost dána implementací typu size_t
, jedná se o bezznaménkové celé číslo (integer
). Ve standardu C99 a následujících, je možné použít konstantu SIZE_MAX
z knihovny <stdint.h>
.
Rozšíření a alternativy
Implementace knihovny jazyka C pro různé systémy, dává možnost kompilátorům nabídnout jiné možnosti a rozšíření k základnímu balíčku malloc()
funkcí:
alloca()
– funkce alokuje určený počet bytů na zásobník, neexistuje žádá možnost uvolnění. Její používání není doporučeno.[12] Standard C99 umožňuje deklarovat pole s proměnnou délkou a poskytuje lepší mechanismy pro alokování paměti na zásobníku.POSIX
definuje funkciposix_memalign
Odkazy
Reference
V tomto článku byl použit překlad textu z článku C dynamic memory allocation na anglické Wikipedii.
- ISO/IEC 9899:1999 specification. [s.l.]: [s.n.] Dostupné online. S. p. 313, § 7.20.3 "Memory management functions". (anglicky)
- GODSE, Atul P.; GODSE, Deepali A. Advanced C Programming. p. 6-28: Technical Publications, 2008. ISBN 978-81-8431-496-0. S. 400. (anglicky)
- SUMMIT, Steve. C Programming Notes - Chapter 11: Memory Allocation [online]. [cit. 2011-10-30]. Dostupné v archivu pořízeném dne 2011-11-06. (anglicky)
- STROUSTRUP, Bjarne. Programming: Principles and Practice Using C++. 1009, §27.4 Free store: Addison Wesley, 2008. ISBN 978-0-321-54372-1. S. 1236. (anglicky)
- gcc manual [online]. gnu.org [cit. 2008-12-14]. Dostupné online. (anglicky)
- BARRY, Peter; CROWLEY, Patrick. Modern Embedded Computing: Designing Connected, Pervasive, Media-rich Systems. [s.l.]: Elsevier Dostupné online. ISBN 9780123914903. S. 179. (anglicky)
- alloca [online]. 5 September 2006 [cit. 2011-09-18]. Dostupné online. (anglicky)
- Casting malloc [online]. Cprogramming.com [cit. 2007-03-09]. Dostupné online. (anglicky)
- http://clang.llvm.org/doxygen/MallocSizeofChecker_8cpp_source.html
- FAQ list · Question 7.7b [online]. C-FAQ [cit. 2007-03-09]. comp.lang.c Dostupné online. (anglicky)
- Shared libraries Archivováno 27. 5. 2015 na Wayback Machine.
- AMARASINGHE, Saman; LEISERSON, Charles. 6.172 Performance Engineering of Software Systems, Lecture 10 [online]. Massachusetts Institute of Technology, 2010 [cit. 2015-01-27]. Dostupné v archivu pořízeném dne 2015-06-22. (anglicky)