Učíme se C (18. díl) - Dynamická alokace paměti - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:



C/C++

Učíme se C (18. díl) - Dynamická alokace paměti

7. února 2001, 00.00 | Dnes se dozvíte, co je to dynamická alokace paměti a jak s takovou pamětí pracovat. Dále si popíšeme
operátor sizeof a funkce pro přidělení a uvolnění paměti.

Dynamická alokace paměti

Až do teď jsme pracovali pouze s tzv. staticky alokovanou pamětí, tedy s paměťovými objekty, o jejíž alokaci se staral náš přeložený program sám. Pokud jsme například v programu nadefinovali proměnnou, program pro ni automaticky alokoval paměť a po skončení její životnosti tuto paměť zase uvolnil. Problém ale nastává v okamžiku, kdy v programu potřebujeme pracovat s daty, pro která nemůže být paměť alokována staticky, například proto, že předem není známá její potřebná velikost. V takových případech se používá tzv. dynamická alokace paměti, tedy taková, kdy sám programátor může v programu rozhodnout, kolik paměti alokovat. Ještě před samotnými příkazy pro alokaci paměti se ale podíváme na operátor sizeof, který právě při dynamické alokaci často využijeme.

Operátor sizeof

O operátoru sizeof jsme se už párkrát zmínili. Dnes se na něj podíváme trochu blíže. Už víme, že tento operátor slouží k zjišťování velikosti svého operandu. Existují dva typy operandů, na které můžeme operátor sizeof aplikovat. Je to buď identifikátor některého datového typu:

sizeof( identifikátor_datového_typu )

nebo libovolný výraz:

sizeof výraz

V případě, že operandem bude identifikátor některého datového typu (musí být uveden v závorkách), je výsledkem vyhodnocení velikost, jakou by v paměti zabíral objekt (např. proměnná) tohoto typu. Množina použitelných typů, na které lze sizeof aplikovat, není nijak omezena a je možné použít tento operátor i na uživatelsky definované typy.

Vyzkoušejte sami, jak budou vyhodnoceny následující výrazy:

sizeof(int)       //velikost int
sizeof(int*)      //velikost ukazatele na int
sizeof(int [10])  //velikost pole deseti intových položek
sizeof(struct {int x; char a;})  //velikost zadané struktury

Druhý případ, kdy operátor sizeof použijeme na výraz, je podobný. Operátor v tomto případě vrací velikost paměti, jaká je potřeba k uložení výsledku vyhodnocení předaného výrazu. Tedy, je-li výsledkem vyhodnocení výrazu hodnota typu int, pak operátor sizeof, aplikovaný na tento výraz, vrací velikost datového typu int.

Př.

double d;
sizeof 2         // jako sizeof(int)
sizeof 2L        // jako sizeof(long int)
sizeof (2/4.0)   // jako sizeof(double)
sizeof (d==4.0)  // jako sizeof(int)

V obou uvedených případech použití sizeof, představovala hodnota získaná vyhodnocením operátoru velikost paměti v bytech.

Důvod, proč operátor sizeof používat, je zřejmý. Velikost jednotlivých datových typů v jazyce C totiž není pevně dána, a tedy se může v různých implementacích překladačů lišit. Typickým příkladem je typ int, který u 16-bitových překladačů býval dvoubajtový, kdežto novější překladače ho implementují jako čtyřbajtový. Pokud bychom chtěli dynamicky alokovat pole deseti hodnot typu int, řešili bychom problém, zda alokovat 20 bytů nebo 40. S pomocí operátoru sizeof můžeme s klidným srdcem alokovat 10*sizeof(int) bytů. Pokud překladač využívá, kvůli zrychlení přístupu k paměti, tzv. zarovnávání paměti (memory alignment), může být i velikost některých strukturovaných typů větší, než jakou bychom asi předpokládali (což by mělo být vidět i na výše uvedeném příkladu sizeof(struct {int x; char a;}) ).

Funkce malloc()

Pro dynamickou alokaci paměti můžeme použít funkci malloc(), jíž předáme jediný parametr, počet bytů, kolik chceme v paměti alokovat.

void *malloc(unsigned int size)

Funkce se pokusí alokovat souvislý blok paměti veliký size bytů a v případě úspěchu vrátí void ukazatel na začátek takto získané paměti. V případě, že se alokace nezdaří, například kvůli nedostatku paměti, vrací funkce hodnotu NULL. Je více než vhodné návratovou hodnotu funkce uchovat, aby pak bylo možné naalokovanou paměť zase uvolnit (viz. níže). Při přiřazování návratové hodnoty funkce malloc je ale třeba si uvědomit, že jde o generický (void) ukazatel, a tudíž bychom ho měli vždy přetypovat na příslušný bázový typ. Přiřazení bez přetypování je sice také možné, ale vzhledem k tomu, že v C++ by již šlo o chybu, je vhodné v tomto případě přetypovávat.

V souvislosti s používáním funkce malloc je třeba si uvědomit i fakt, že skutečně přidělená paměť může být o něco větší, než kolik jsme požadovali, což se může nepříjemně projevit v případě, alokujeme-li mnoho kratších úseků paměti.

Příklad alokace jednoduché proměnné typu double s testem úspěšnosti:

double *p;

if ((p = (double*)malloc(sizeof(double)))==NULL) 
     { printf("Nedostatek paměti");
       exit (1);
     }

S nově alokovanou pamětí pak můžeme pracovat prostřednictvím dereferovaného ukazatele p.

*p=4.0;
printf("%f", *p);

Funkce calloc()

Stejnou úlohu jako funkce malloc zastává i podobná funkce calloc. Rozdílem je pouze to, že calloc je definována se dvěma parametry, díky kterým je o něco vhodnější pro dynamickou alokaci polí.

void *calloc(unsigned int n, unsigned int size)

První parametr n představuje počet položek pole a druhý velikost jedné položky v bytech. Prakticky se tedy naalokuje n*size bytů, ke kterým pak pomocí pointeru můžeme přistupovat třeba jako k poli.

Budeme-li chtít alokovat pole deseti prvků typu double, uděláme to pomocí funkce calloc takto (pro jednoduchost vynechávám test úspěšnosti alokace):

double *p;
int i;
p = (double *)calloc(10, sizeof(double));

Pomocí funkce malloc bychom stejnou alokaci provedli takto:

p = (double *)malloc(10 * sizeof(double));

Vzhledem k zaměnitelnosti pole a ukazatele (viz. minulý díl) můžeme pointer p považovat přímo za identifikátor pole a podle toho s ním pracovat.

//vynulování pole
for (i=0; i<10; i++) p[i]=0;

Funkce free()

Pro uvolnění dříve alokované paměti slouží v jazyce C funkce free.

void free(void *ptr)

Jako svůj jediný parametr potřebuje tato funkce ukazatel na začátek dříve alokované paměti, kterou uvolní. Je-li funkci předán NULL pointer, nestane se nic. Funkce free nevrací žádnou návratovou hodnotu. Jisté nebezpečí spočívá v tom, že hodnota ukazatele, předaného funkci free, zůstává i po uvolnění paměti zachována, a tedy je možné i nadále pracovat s pamětí, kterou tento pointer označuje. V tu dobu již ale tato paměť může být využívána z jiných částí programu, nebo i úplně jinými programy. Proto je nanejvýš vhodné vynulovat (přiřadit hodnotu NULL) takovýto ukazatel ihned po zavolání funkce free().

Dobrou zásadou programátora by mělo být také důsledné uvolňování alokované paměti ihned, jakmile již není potřeba, abychom zbytečně nezabírali prostředky, které se nám, nebo jiným spuštěným programům, mohou později hodit.

V případě, že jsme paměť alokovali pomocí funkce calloc, je někdy nutné uvolnit ji, namísto funkce free, funkcí cfree.

Příklad alokace pole struktur a následné uvolnění:

struct point { int x;
               int y;
             } *p;

p = (struct point  *)malloc(10 * sizeof(struct point));
...
free(p);

p[3].x=4; //nepřípustné, zásah do paměti, která nám již nepatří.

p=NULL; //pro jistotu nepotřebnou adresu zapomeneme

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » C/C++  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: