next up previous contents
Elõre: Értékadás mutatóknak Fel: Mutatók és tömbök Vissza: Mutatók és tömbök

A mutatók használata

A mutatók a C nyelvben bármilyen típusú adatra, vagy bármilyen típust visszaadó függvényre mutathatnak. A mutató fontos jellemzõje a méretén és az értékén kívül az is, hogy pontosan milyen típusra mutat. Természetesen egy összetettebb mutatótípus értelmezésekor könnyen el lehet tévedni. Hasznos "ökölszabály" szintaktikailag a következõ kijelentés: minden helyen, ahol adott tárolási egységet megnevezõ azonosító állhat, ott zárójelben állhat egy mutatóazonosító az indirekció operátorát (egyoperandusú *) követõen. Például az i azonosító elõfordulhat a következõ környezetekben:

            short i;
            i = 8;
            j = i + 2;
            i++;
            j = fugg(i);
tehát i helyére mondjuk *ptr-t írva a következõ utasítások is helyesek szintaktikailag:
            short (*ptr);
            ...     
            (*ptr) = 8;
            j = (*ptr) + 2;
            (*ptr)++;
            j =  fugg((*ptr));
(természetesen a hivatkozott mutatót körülvevõ ( ) zárójelpárok - az utolsó elõtti utasítást kivéve - a precedenciaszabályok figyelembevételével elhagyhatók). Szemantikailag az elsõ példa definiálja a ptr változót, mint egy rövid egészre mutató pointert. Ezt a deklarációt a zárójelek elhagyásával is írhatjuk: short *ptr.

Ezzel a deklarációval teljesen egyenértékû a 1.4.7-ból már ismert példa:

            typedef short *short_ptr 
            ...
            short_ptr ptr;
Az elõzõ példa második sorának értékadó utasítása a ptr által megcímzett, short-nak tekintett tárterületre beírja a 8 értéket. (Meg kell jegyeznünk, hogy ebben az esetben ptr valamilyen érvényes címet kell, hogy tartalmazzon, mert egyébként a (*ptr) = 8 értékadás következménye beláthatatlan lesz. A példában szereplõ ... helyére kell képzelnünk azt a müveletet, amelynek során a ptr pointerbe egy valamilyen módon lefoglalt, egyetlen short tárolására alkalmas memóriaterület címe kerül.)

A harmadik példában a mutatón keresztüli indirekcióval nyert értéket használjuk fel egy kifejezésben, a negyedikben megnövejük eggyel a rövid egésznek tekintett, a ptr által megcímzett adatot, míg az utolsó utasítás az indirekcióval nyert értéket adja át egy függvénynek. Kicsit bonyolultabb példák a bevezetõben említett "ökölszabály" alkalmazására:

a)
int func(double, int); tex2html_wrap_inline11411 int (*pfunc)(double, int);

Ez a példa azt illusztrálja, hogy deklarálhatunk egy függvényre mutató pointert. Esetünkben pfunc egy  "int típusú értéket visszaadó, egy double és egy int típusú paramétert váró függvényre mutató pointer" típusú változó. A (*pfunc) körüli zárójelek nem hagyhatók el, mert akkor pfunc-ot egy olyan függvénynek deklarálnánk (és valószínûleg sehol sem definiálnánk), amelyik egészre mutató pointert adna viszsza. Függvényre mutató pointerek esetén is célszerû a típust typedef-fel definiálni:

            typedef int (*func_ptr)(double, int)
            ...
            func_ptr pfunc;
Egy adott fajta függvényre mutató típust minden függvény-fajta esetében egyedileg kell "összeraknunk" . A típusmódosító operátorokban való gondolkodás itt is sokat segít: Az új típus a func_ptr lesz. Ezt az int-bõl származtatjuk a következõképpen. A (double, int) postfix típusmódosító operátorral elõállítunk egy - önmagában semmire sem használható - int-et visszadó függvény típust. Ebbõl a típusból a * operátor segítségével létrehozzuk a végsõ, típust: egy ilyen függvényt megcímzõ pointer típusát. Fontos a zárójelezés. Ha a fenti példában a *func_ptr-t nem védenénk zárójelekkel, akkor egy int * típusú visszatérési értékkel rendelkezõ függvénytípust definiálnánk. (A ( ) típusmódosító operátor precedenciája magasabb, mint a * típusmódosító operátoré, lásd a 1.4. táblázatot.) Természetesen érthetõbbé válik minden, ha a fenti típusdeklarációt részeire bontjuk:
            typedef int    int_fv(double, int);
            typedef int_fv *func_ptr;
b)
a = func(0.0, 81); tex2html_wrap_inline11411 a = (*pfunc)(0.0, 81);

Ezzel a példával azt igyekszünk megmutatni, hogy "ökölszabályunkat" alkalmazva hogy aktivizálhatunk indirekt módon függvényeket. A pfunc változó típusa az a) példa alapján (akár a typedef-es deklarációt, akár az anélküli deklarációt tekintve) "egész értéket visszaadó, egy dupla pontosságú valós, és egy egész típusú paramétert váró függvényre mutató" típus. Az indirekción alapuló függvényhívás szemantikája a következõ: A függvényre mutató pointerre alkalmazzuk az indirekció operátorát, a *-ot. Ekkor - az általános szabályoknak megfelelõen - a *pfunc kifejezés nem más, mint maga a tárolási egység, mely esetünkben egy függvény. Egy "függvény típusú" kifejezéssel nem tudunk mit kezdeni, úgyhogy az indirekciót követõen alkalmazzuk az függvényaktivizáló operátort, a ( )-et (természetesen a szükséges függvényargumentumokkal együtt). Mivel ez utóbbi operátor precedenciája magasabb, mint az indirekció operátoré, *pfunc kifejezést zárójelekkel kell védenünk.

c)
float vektor[20] tex2html_wrap_inline11411 float *(pvekt[20]);

Itt float típusú tárolási egységekre mutató pointerek tömbjét deklaráltuk. A short_ptr deklarációjához hasonlóan egy kissé logikusabban átcsoportosítva az egyes típusmódosító operátorokat, pvekt-et így is deklarálhatjuk: float* pvekt[20] Ennek a példának a logikája nagyon hasonlít az a) pontban leírtakhoz. Jelen esetben a módosított typedef-es alak a következõ lehet:

               typedef float* float_vekt_ptr[20]
               ...
               float_vekt_ptr pvekt;
vagyis a megnevezett új típus a flaot_vekt_ptr. Ezt a float* típusból származtatjuk úgy, hogy alkalmazzuk a postfix [20] tömbtípus képzõ operátort.
d)
x = vektor[2] tex2html_wrap_inline11411 x = *(pvekt[2]);

Most azt láthatjuk, hogy a c) pont szerint deklarált pvekt változó felhasználásával hogy érhetünk el egyes adatokat. Elõször a [2] indexelõ operátorral elõállítjuk pvekt megfelelõ elemét (mely természetesen float* típusú), majd az indirekció operátorát alkalmazva megkapjuk a kívánt lebegõpontos értéket.

A short *(*pptr); példa "ökölszabályunk" rekurzivitását mutatja be, azaz egy mutató definíciójában lecserélve az azonosítót egy (itt feleslegesen) zárójelezett indirekt pointerre, sikerült egy mutatót megcímzõ pointert definiálnunk. Természetesen ezt tovább, tetszõleges szintig folytathatnánk, legalábbis elvileg.

Természetesen nagyon körültekintõen kell eljárnunk az összetettebb típusú pointerek deklarálásakor. Ha a fenti c) példánál a zárójeleket máshova tesszük ki, egészen másfajta változó lesz a pvekt: a float (*pvekt)[20] deklaráció értelmében pvekt egy 20 elemû float típusú tömbre mutató pointer lesz.


next up previous contents
Elõre: Értékadás mutatóknak Fel: Mutatók és tömbök Vissza: Mutatók és tömbök