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:
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;
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.
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.
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.
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.