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!