Přeskočit na obsah

Dynamická alokace paměti v jazyce C

Z Wikipedie, otevřené encyklopedie

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

[editovat | editovat zdroj]

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

[editovat | editovat zdroj]

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í

[editovat | editovat zdroj]

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()

[editovat | editovat zdroj]

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í

[editovat | editovat zdroj]

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

[editovat | editovat zdroj]

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í

[editovat | editovat zdroj]
  • Používání přetypování může umožnit program nebo funkci z jazyka C kompilovat jako program v jazyce C++
  • Přetypování umožní použití starší ANSI C verze jazyka C, kde funkce malloc() vrací typ char *[8]
  • Přetypování umožňuje vývojáři identifikovat chyby v typových nesrovnalostech[9]

Nevýhody přetypování

[editovat | editovat zdroj]
  • 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 se malloc() nachází.[8][10] Pokud tento prototyp malloc() chybí, standardně kompilátor považuje návratovou hodnotu z malloc() jako int. 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 kde long a ukazatele bývají 64bitové a int 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

[editovat | editovat zdroj]

Š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í funkce free(), 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()

[editovat | editovat zdroj]

Protože malloc() a jeho alternativy mohou 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

[editovat | editovat zdroj]

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

[editovat | editovat zdroj]

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 funkci posix_memalign

V tomto článku byl použit překlad textu z článku C dynamic memory allocation na anglické Wikipedii.

  1. a b ISO/IEC 9899:1999 specification. [s.l.]: [s.n.] Dostupné online. S. p. 313, § 7.20.3 "Memory management functions". 
  2. GODSE, Atul P.; GODSE, Deepali A. Advanced C Programming. p. 6-28: Technical Publications, 2008. ISBN 978-81-8431-496-0. S. 400. 
  3. SUMMIT, Steve. C Programming Notes - Chapter 11: Memory Allocation [online]. [cit. 2011-10-30]. Dostupné v archivu pořízeném dne 2011-11-06. 
  4. STROUSTRUP, Bjarne. Programming: Principles and Practice Using C++. 1009, §27.4 Free store: Addison Wesley, 2008. ISBN 978-0-321-54372-1. S. 1236. 
  5. gcc manual [online]. gnu.org [cit. 2008-12-14]. Dostupné online. 
  6. BARRY, Peter; CROWLEY, Patrick. Modern Embedded Computing: Designing Connected, Pervasive, Media-rich Systems. [s.l.]: Elsevier Dostupné online. ISBN 9780123914903. S. 179. 
  7. alloca [online]. 5 September 2006 [cit. 2011-09-18]. Dostupné online. 
  8. a b Casting malloc [online]. Cprogramming.com [cit. 2007-03-09]. Dostupné online. 
  9. http://clang.llvm.org/doxygen/MallocSizeofChecker_8cpp_source.html
  10. FAQ list · Question 7.7b [online]. C-FAQ [cit. 2007-03-09]. comp.lang.c Dostupné online. 
  11. Shared libraries Archivováno 27. 5. 2015 na Wayback Machine..
  12. 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. 

Související články

[editovat | editovat zdroj]