9. Ukazovatele a polia
9.3 Statické a dynamické pole
Ak deklarujeme pole pomocou [ ], čiže jeho veľkosť je známa už v čase prekladu, ide vždy o statické pole
bezohľadu na to, či sa táto deklarácia nachádza vo vnútri funkcie alebo
nie. Pole, pre ktoré sa pamäť alokuje v priebehu vykonávania programu
sa nazýva dynamické. Ak už raz máme pre pole pamäť k
dispozícii, nezáleží na tom, či sa alokovala staticky alebo dynamicky,
s poľom sa pracuje rovnako - môžeme používať klasický zápis
(pomocou indexovanej premennej) alebo využiť možnosti, ktoré nám dávajú
pointery a pointerová aritmetika.
Ten
istý príklad ešte raz, tento raz však použijeme dynamické pole
a pointerovú aritmetiku. Postup riešenia je však (až na alokáciu
pamäte) totožný:
void main(void)
{
int *p; /* deklarujeme p ako pointer na int */
int n, i, max;
printf("pocet prvkov n = ");
scanf("%d\n", &n);
/* dynamicky alokujeme pamäť potrebnú pre pole */
p = (int*)malloc(n*sizeof(int));
for (i = 0; i < n ; i++) {
printf("%d. prvok: ", i + 1);
scanf("%d", p + i); /* p + i je adresa! */
}
max = *p; /* alebo tiež max = *(p+0) */
for (i = 0; i < n ; i++) {
if (*(p + i) > max) max = *(p + i);
}
printf("Maximum je %d \n", max);
}
Deklarovali sme p ako pointer na int. Týmto sme ale vyhradili iba 4 bajty pre pointer p , žiadna pamäť pre pole sa ešte nealokovala. To musí programátor zabezpečiť sám. Použili sme preto funkciu malloc , po jej volaní (kvôli úspore miesta bez testovania úspešnosti pridelenia pamäti :-) pointer p ukazuje na začiatok bloku pamäti vyhradeného pre pole - na prvý prvok tohto poľa.
Pointer p môžeme považovať za dynamické pole!
Výhodou je, že pre pole takto rezervujeme len toľko pamäte, koľko je skutočne potreba.
Pristupovať k prvkom poľa môžeme dvoma spôsobmi, oba zápisy sú úplne rovnocenné:
Prvok poľa s indexom 0: | *(p + 0) == p[0] == *p | Prvok poľa s indexom 1: | *(p + 1) == p[1] | Prvok poľa s indexom i: | *(p + i) == p[i] |
p + i je adresa prvku poľa s indexom i, *(p + i) je jeho hodnota
Aj keď používame pole statické, môžeme pokojne použiť "pointerový" zápis. Majme takúto deklaráciu:
int a[6], *b;
a je statické pole šiestich prvkov, b je pointer na int, zatiaľ neukazuje na nič zmyslupné
b = (int*) malloc(6*sizeof(int));
b je dynamické pole, má tiež 6 prvkov, pointer b ukazuje na prvý prvok poľa
adresa prvku s indexom i: | &a[i] == a + i &b[i] == b + i | prvok s indexom i
| a[i] == *(a + i) *a == a + 0 == a[0] b[i] == *(b + i) *b == b + 0 == b[0] |
Do
statického poľa sa zvykne pristupovať pomocou indexov a do
dynamického poľa pomocou pointerovej aritmetiky, ale záleží len na
programátorovi, aký spôsob si vyberie (pokojne si môže vybrať aj jeden
so spôsobov a používať vždy len ten). Prístup k prvkom poľa
pomocou pointerov býva obyčajne efektívnejší. Pri indexácií sa totiž
tiež musí vypočítať najprv adresa príslušného prvku poľa podľa vzťahu
&a[i] == bázová adresa a + i*sizeof(int),
čo je časovo náročnejšie ako pripočítavať k pointeru len nejakú konštantu: a + i.
Väčšina kompilátorov však zvyčajne prístup pomocou indexov aj tak
prevedie na prístup pomocou pointerov, takže vnútorne statické
i dynamické pole vlastne "funguje" rovnako.
Dôležitý rozdiel!
a je tiež pointer ale konštantný, čo znamená, že nemôže byť zmenený!
Napr. priradenie
a = b
je vylúčené!
Avšak naopak t.j. priradenie
b = a;
je v poriadku. Premenná b
je predsa len "obyčajný" pointer na int, nemusí ukazovať len na prvky
pôvodného poľa. Pozor ale na to, aby ste zmenou pointeru nechtiac
nestratili prístup k poľu!
Ak potrebujeme alokovať pamäť pre n prvkov, z ktorých každý má rovnakú veľkosť, môžeme použiť aj funkciu
calloc(n, velkost),
ktorá alokuje toto pole prvkov rovnako ako príkaz
malloc(n * velkost)
a naviac
ho vynuluje! Tiež vráti pointer na začiatok prideleného bloku pamäti
alebo NULL, ak sa požadovanú pamäť nepodarilo prideliť. V niektorých
systémoch je však nutné takto alokovanú pamäť uvoľniť pomocou funkcie cfree!
|