3. Ukazovatele v C
3.1 Naučme sa syntax
Ak chceme použiť v programe ukazovateľ, musíme si najprv nejaký deklarovať (rovnako ako akúkoľvek inú premennú). Ukazovateľ je vždy zviazaný s typom, ktorého premenné sprístupňuje, na ktoré ukazuje. Miesto termínu ukazovateľ by sa malo preto kvôli jednoznačnosti vždy uvádzať ukazovateľ na „nejaký“ typ (tzv. doménový typ ukazovateľa). Podľa typov premenných teda rozlišujeme aj typy ukazovateľov. Existuje ukazovateľ na int, na char, ako aj ukazovateľ na ľubovoľný iný jednoduchý či štruktúrovaný údajový typ. Na ukážku deklarujme ukazovateľ na premenné typu int. Deklarácia ukazovateľov na iné typy je analogická.
int i; |
Deklarovali sme premennú i typu int. |
int *p_i; |
Pridaním symbolu * pre identifikátor premennej p_i sme kompilátoru dali na známosť, že nejde o obyčajnú premennú typu int, ale že p_i je ukazovateľ na premennú typu int. S podobným zápisom ste sa už určite stretli pri práci so súbormi. Spomínate si na FILE *f; /code>? |
Aj keď je voľba identifikátorov premenných plne v kompetencii programátora, odporúča sa voliť mená ukazovateľov tak, aby bolo na prvý pohľad jasné, že ide o ukazovateľ. Je to síce maličkosť, ktorá nie je povinná, ale keďže výrazne zvyšuje prehľadnosť programu, zvykne sa dodržiavať. Identifikátory ukazovateľov možu začínať napr. znakmi p, p_ , ptr, uk atď.
|
Všimnime si ešte raz rozdiel v zápisoch (deklarácia a jej význam):
char c; |
c je premenná typu char |
char *p_c; |
p_c je ukazovateľ na premenné typu char |
float f; |
f je premenná typu float |
float *p_f; |
p_f je ukazovateľ na typ float |
unsigned long x; |
x je premenná typu unsigned long |
unsigned long *p; |
p je ukazovateľ na typ unsigned long |
Samozrejme, že môžeme deklarovať a potom používať aj ukazovateľ na údajový typ, ktorý sme si definovali sami, napr. pomocou typedef takto:
typedef struct {
char meno[100];
int vek;
float plat;
} TOsoba;
TOsoba s; |
s je premenná typu TOsoba |
TOsoba *p_s; |
p_s je ukazovateľ na typ TOsoba |
A môžeme ísť ešte ďalej. Čo takto ukazovateľ na typ ukazovateľ?
int p; |
p je pointer na int |
int **p; |
p je pointer na „pointer na int“ |
Prečo? Predstavu nám uľahčí malý trik s umiestnením symbolu *. Zápis int *p znamená pre prekladač presne to isté ako int* p . V súlade s tým, ako sa deklarujú obyčajné premenné, môžeme teda povedať, že p je typu int*. Keďže * pred menom premennej znamená, že ide o ukazovateľ na určitý typ, zápis int* *p potom logicky hovorí, že p je ukazovateľ na typ „ukazovateľ na int.“ Keďže na umiestnení * nezáleží, používa sa zväčša deklarácia v tvare int **p; resp. int** p; . Premenná p teraz uchováva adresu ukazovateľa, ktorý ukazuje na premennú typu int.
Ale tu to nekončí. Vyskúšajte svoj obľúbený céčkovský kompilátor takouto deklaráciou: int *****p; Určite pochopí, že ide o pointer na pointer na pointer na pointer na pointer na typ int. Pravdepodobnosť, že by ste niekedy premennú takého typu potrebovali je síce malá, ale čo ak predsa? 🙂
Často budeme chcieť deklarovať viacero ukazovateľov naraz. Zápisom
int *p1, p2, p3; čo je pre prekladač to isté ako
int (*p1), p2, p3; sme však deklarovali iba jeden ukazovateľ na int. Premenné p2 a p3 sú „len“ premenné typu int.
Správny je preto zápis:
int *p1, *p2, *p3; Až teraz sú premenné p1, p2 a p3 všetko ukazovatele na typ int.
Taktiež môžeme pokojne definovať nový typ ukazovateľ na int. Pri deklarácii sa však už symbol * nenachádza, čo môže na prvý pohľad pôsobiť mätúco. (Ale „proti gustu žiaden dišputát.“ 🙂)
typedef int *TPtr;
TPtr p1, p2, p3;
|