Kurz C++ (6.) V této lekci dostáváme možná k nejobtížnější problematice programování a to k ukazatelům. Čtenář se za prvé dozví, co to je ukazatel, dále se dozví, jak ukazatel získá a jak s ním pracuje. UkazateleKaždá proměnná, kterou v programu deklarujeme, se nachází v paměti počítače, která je rozdělena na buňky o velikosti jednoho bytu, tedy 8 bitů. Každá buňka má nějaké číslo, pomocí kterého ji lze jednoznačně určit. Tomuto číslu se říká adresa. Ukazatel (angl. pointer) je proměnná nebo konstanta, která v sobě udržuje adresu nějaké jiné proměnné (říká se, že ukazatel "ukazuje na proměnnou"), nebo i adresu nějaké libovolné buňky v paměti. Ukazatelem je možné číst nebo měnit hodnotu na adrese, kam ukazuje. Jako vždy, ukážeme si příklad:
Nejdříve jsme deklarovali ukazatel Dále jsme ukazatel nastavili na adresu proměnné Na dalším řádku jsme nepřímo přiřadili proměnné Ukazatel může být pevného datového typu, to znamená, že ukazatel "ví", na jaký datový typ ukazuje, a při dereferenci vrátí přesně ten datový typ. Existuje ještě jeden typ ukazatele, tzv. obecný, který může ukazovat na proměnnou jakéhokoli datového typu, ale datový typ "si nepamatuje", a z tohoto důvodu nemůže být na něm provedena dereference. Obecný ukazatel se deklaruje klíčovým slovem void:
Kompilátor ohlásí chybu na řádku Ukazatel může být i prázdný, tedy neukazuje na žádnou adresu. K tomu se používá zvláštní hodnota
NULL, v C++ lze používat i 0.
Možná se ptáte, k čemu vlastně ukazatele jsou. Je pravda, že jsme si neukázali žádné jejich smysluplné použití. Dále vysvětlím souvislost mezi ukazateli a poli. Až si vysvětlíme záznamy ukážeme si další použití významné ukazatelů a záznamů. Dále v kurzu probereme třídy a tam také uvidíte, jak jsou ukazatele důležité. Ukazatele a polePole a ukazatele mají v jazycích C a C++ k sobě velice blízko. Bez ohledu na rozdílnou syntaxi se tyto jazyky dívají na pole a ukazatele stejně: pole je vlastně ukazatel někam do paměti, kde se nachází seznam proměnných stejného typu těsně za sebou. Řekli jsme si, že se meze polí nekontrolují. Teď chápeme, proč tomu tak je: pole je ukazatel, který pouze ukazuje na proměnnou na nějaké adrese, ale nelze říci, kolik je za ní dalších proměnných stejného typu, to ví pouze programátor. Také jsme si řekli, že velikost prvku pole získáme zápisem Ukazovali jsme si, že řetězce lze deklarovat zápisem: Ze skutečností výše uvedených vyplývá i to, že s ukazatelem můžeme zacházet jako s polem,
například:
Existují i případy, kdy pole a ukazatel nejsou totéž. Typický případ je použití operátoru
sizeof:
Také je nutné si uvědomit rozdíl mezi deklarací pole (popř. řetězce) a deklarací ukazatele.
Aritmetika ukazatelůJednou z předností jazyka C a C++ je aritmetika ukazatelů. To nám dovoluje zacházet s ukazatelem jako s číselnou proměnnou: můžeme k němu přičítat čísla, odečítat, zvětšit a zmenšit, porovnávat s jiným ukazatelem a tak podobně. Například aritmetikou ukazatelů můžeme nahradit použití hranatých závorek (to většinou neděláme, ale někdy se nám to může hodit):
Další použití je posuv v řetězci:
Typické použití je procházení řetězce za účelem nějakého zpracování. Představte si, že bychom potřebovali vypsat
řetězec tak, že přeskakujeme nadbytečné mezery (tj. když jich je více za sebou, vypíšeme jen jednu). Nejdříve musíme vymyslet algoritmus (jak to budeme
provádět): projdeme všechny znaky pole a pokud znak není mezera vypíšeme ho, jinak ho vypíšeme pouze pokud předchozí zpracovaný znak nebyl mezera:
Poznámka: snažil jsem se napsat funkci vypis() tak, aby byla pochopitelná. Ovšem jazyk C++ je známý pro svou stručnost a eleganci, takže si ukážeme, jak naši funkci napsat v tomto duchu. Podmínka cyklu while ( *s != 0 ) bude mít
hodnotu false (0) pokud hodnota *s bude 0, a hodnotu true (1, ale také jakákoli nenulová hodnota)
pokud hodnota *s bude různá od 0. To znamená, že hodnota podmínky je stejná s hodnotou *s . To využijeme k tomu,
abychom napsali cyklus while takto: while (*s) . Je to naprosto totéž.
Dále zaměříme svou pozornost na příkazy if. Trochu vadí, že příkaz pro vypsání se opakuje dvakrát, takže zkusíme napsat podmínku
pro if tak, abychom si vystačili s jedním
if. Platí, že se znak vypíše pokud není mezera nebo pokud mezera je a současně posledně
zpracovaný není mezera. Příkaz if, který tomu odpovídá, je
Ukazatele a funkceKdyž jsme si ukázali funkce pro práci s řetězci uvedl jsem jejich syntaxi, ale značně zjednodušeně. Nyní, vyzbrojení znalostmi o ukazatelích, si můžeme
ukázat hlavičky těchto funkcí. Funkce pro zpracování řetězců mohou dostat jako parametry i řetězce značné velikosti. Kdyby se musel funkci předat
takový řetězec tak, že by se "do funkce" zkopíroval celý, mohlo by to trvat dost dlouho. Proto existuje mnohem lepší způsob předávání řetězců
(a nejenom, ale jakýchkoliv proměnných velkých datových typů): funkci se předá pouze ukazatel na řetězec. Například:
To byl v jazyce C jediný způsob předávání odkazem. V jazyce C++ máme lepší způsob, a to je operátor
& (operátor reference - angl. reference operator),
jak jsme si ukázali minule.
Reference v sobě udržuje adresu nějaké proměnné, ale syntakticky se chová jako ta proměnná. Ale jaký je rozdíl mezi předávání parametru
ukazatelem a referencí? Je to jednoduché, nulová reference neexistuje, nulový ukazatel ano. Tedy předávání ukazatelem můžeme použít tam, kde
chceme mít možnost v parametru nepředat nic. Jako příklad vylepšíme funkci
prumery():
Poznámka: význam klíčového slova const u parametrů string2 a strSource v deklaraci výše uvedených funkcí znamená,
že funkce nemění řetězce předané v těchto parametrech, takže lze předat i konstantní řetězec (deklarovaný také
klíčovým slovem const - const char *str = "Ahoj"; ). Bez klíčového slova const by to možné nebylo.
To je pro tento měsíc všechno. Pilujte, zkoušejte, nebojte se experimentovat, za měsíc nashledanou.
|
|
© 2001
Vogel
Publishing, design by
ET NETERA