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; span class=kom>/* 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; span class=kom>/* 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äte 🙂) pointer p ukazuje na začiatok bloku pamäte 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[5], *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äte 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!
|