Učebnice Programování V C/C++ PROGRAMOVÁNÍ V C/C++ - 1. DÍL................................................................................................................. 3 PŘEDPOKLADY .............................................................................................................................................. 3 CO JE TO PROGRAMOVÁNÍ ............................................................................................................................... 3 "KDE SEŽENU TEN PROGRAM?" ........................................................................................................................ 3 INSTALACE A BLIŽŠÍ SEZNÁMENÍ ........................................................................................................................ 3 CO " TEN PROGRAM" DĚLÁ ............................................................................................................................... 3 NA ZÁVĚR PRVNÍHO DÍLU ................................................................................................................................. 3 PROGRAMOVÁNÍ V C/C++ - 2. DÍL................................................................................................................. 4 NÁŠ PRVNÍ PROGRAM ..................................................................................................................................... 4 KOMENTÁŘE ................................................................................................................................................. 4 ŘÁDKY CO ZAČÍNAJÍ # ..................................................................................................................................... 4 VLASTNÍ PROGRAM ........................................................................................................................................ 4 J EŠTĚ TROCHU HYGIENY ................................................................................................................................. 5 CO SI ZAPAMATOVAT ...................................................................................................................................... 5 PROGRAMOVÁNÍ V C/C++ 3. DÍL................................................................................................................... 6 PROMĚNNÉ................................................................................................................................................... 6 ZÁKLADNÍ TYPY PROMĚNNÝCH ......................................................................................................................... 6 JAK SI " UDĚLÁME" TAKOVOU PROMĚNNOU .......................................................................................................... 7 KDE DEKLAROVAT PROMĚNNOU?...................................................................................................................... 7 CO MŮŽEME S ČÍSELNOU PROMĚNNOU DĚLAT? ................................................................................................... 7 D NEŠNÍ JEDNODUCHÝ PROGRAM ...................................................................................................................... 8 PLATNOST PROMĚNNÝCH ................................................................................................................................ 8 DO PŘÍŠTĚ.................................................................................................................................................... 8 PROGRAMOVÁNÍ V C/C++ 4. DÍL................................................................................................................... 9 BLOK KÓDU .................................................................................................................................................. 9 VĚTVENÍ PROGRAMU ...................................................................................................................................... 9 CO KDYŽ POTŘEBUJI VÍCE PODMÍNEK: .............................................................................................................. 11 DALŠÍ KOUZELNÉ SLOVO: ELSE ....................................................................................................................... 11 ZKUŠEBNÍ PROGRAM .................................................................................................................................... 11 DODATEK O PLATNOSTI PROMĚNNÝCH............................................................................................................. 12 PROGRAMOVÁNÍ V C/C++ 1. BONUS .......................................................................................................... 13 PRAVIDLA ................................................................................................................................................... 13 JAK NA TO?................................................................................................................................................. 13 PLÁN ......................................................................................................................................................... 13 PŘÍPRAVA NA SMYČKU .................................................................................................................................. 13 SMYČKA ..................................................................................................................................................... 13 KÓD .......................................................................................................................................................... 13 PROGRAMOVÁNÍ V C/C++ 5. DÍL................................................................................................................. 15 WHILE ...................................................................................................................................................... 15 VARIACE NA WHILE....................................................................................................................................... 15 SMYČKA FOR .............................................................................................................................................. 15 U ČÍME SE ČÍST ............................................................................................................................................ 16 SHRNUTÍ .................................................................................................................................................... 16 PROGRAMOVÁNÍ V C/C++ 6. DÍL................................................................................................................. 17 VYHODNOCOVÁNÍ VÝRAZŮ ............................................................................................................................. 17 L ÍNÉ VYHODNOCOVÁNÍ.................................................................................................................................. 17 FUNKCE ..................................................................................................................................................... 18 V PRAXI...................................................................................................................................................... 18 PROGRAMOVÁNÍ V C/C++ 7. DÍL................................................................................................................. 21 D NEŠNÍ TÉMA .............................................................................................................................................. 21 PREPROCESOR A JEHO DIREKTIVY .................................................................................................................. 21 # INCLUDE< SOUBOR.H> ................................................................................................................................. 21 # INCLUDE"SOUBOR.H" .................................................................................................................................. 21
# DEFINE JMENO HODNOTA ....................................................................................................................... 21 DALŠÍ DIREKTIVY ......................................................................................................................................... 22 N ĚKTERÉ DALŠÍ OPERÁTORY ......................................................................................................................... 22 O PERÁTORY -- A ++ ..................................................................................................................................... 22 PROGRAM .................................................................................................................................................. 23 PŘÍŠTĚ ...................................................................................................................................................... 23 PROGRAMOVÁNÍ V C/C++ 8. DÍL................................................................................................................. 24 POINTERY .................................................................................................................................................. 24 JAK VYPADÁ PAMĚŤ ...................................................................................................................................... 24 PROMĚNNÉ A PAMĚŤ .................................................................................................................................... 24 STACK A HEAP............................................................................................................................................. 24 POINTERY .................................................................................................................................................. 24 JAK SE DEKLARUJE POINTER .......................................................................................................................... 25 KAM MOHOU POINTERY UKAZOVAT .................................................................................................................. 25 SHRNUTÍ .................................................................................................................................................... 25 PROGRAMOVÁNÍ V C/C++ 9. DÍL................................................................................................................. 26 CO JE TO ALOKACE ...................................................................................................................................... 26 JAK SE ALOKUJE .......................................................................................................................................... 26 D EALOKACE................................................................................................................................................ 26 PRÁCE S POINTERY ...................................................................................................................................... 26 NULL A CO JE TAM, KDYŽ TAM NIC NENÍ?? ....................................................................................................... 27 KDYŽ POINTER UKAZUJE NA STACK ................................................................................................................. 27 SHRNUTÍ .................................................................................................................................................... 27 SLUŠNÉ CHOVÁNÍ ........................................................................................................................................ 27 CVIČENÍ ..................................................................................................................................................... 28
Programování v C/C++ - 1. díl Konečně tu máme i něco pro opravdové programátory. Novou rubriku programování otevírá série článků o jazyce C (a později C++). Nepředpokládá žádné dřívější zkušenosti s programováním, přesto je případná znalost php díky velké syntaktické podobnosti výhodou (přestože historicky je původcem této syntaxe právě jazyk C). Vítejte na začátku prvního dílu. Pokud čtete tento článek, chcete se nejspíš naučit programovat. My se v tomto seriálu budeme učit programovat v jazyce C, což je docela vhodný začátek i pokud se rozhodnete s programováním pokračovat později, třeba už profesionálně. Rád bych upozornil, že řada informací je nekompletních, jde mi hlavně o to, aby se začátečníci orientovali.
Předpoklady Předpokladem pro tento seriál je základní znalost počítačů (co je to soubor, spustitelný soubor atp.) a matematiky (výraz, neznámá).
Co je to programování Pokud si nejste jisti tím, co je to programování (vzpomínám si na jednoho známého jak říká "ja stejně vůbec nechápu jako jak se to dělá, že ten počítač hraje hokej"), není se za co stydět. Program je vlastně takový seznam činností (program ;)), které má počítač provést. Taková kuchařka. Vezmi hrnec, dej do něj mléko, začni vařit, přidej puding, dokud není husté tak míchej, přestaň vařit, rozlévej do mističek. Jednoduché!
"Kde seženu ten program?" Tradiční otázka začátečníků je "kde seženu ten program co se to v něm dělá". Ten program co se to v něm dělá je vlastně něco jako textový editor, kam píšete svůj program. Co se týče freewarových programů, doporučuji DevC++ ( stáhnout). Pokud se Vám nějakým způsobem dostalo do ruky Visual C++ nebo Borland C/C++ pro DOS, mohu také doporučit. Já se budu soustředit na Dev-C++, protože je dostupné Vám všem zdarma a bude pro většinu také první volbou.
Instalace a bližší seznámení Instalace by měla být bezproblémová. Vybrat adresář zvládnete určitě sami, volby můžete nechat zaškrtnuté všechny. Po instalaci si založíme nový projekt. V hlavním menu zvolíme Nový->Projekt, za typ zvolíme konzolovou aplikaci/console application, pojmenujeme si ho a uložíme. Některé verze vygenerují už připravený základní zdrojový kód, některé ne (potom bude dobré přidat do projektu nějaký soubor přes Projekt->Nový soubor). Soubory v projektu se zpravidla ukládají s koncovkou .c nebo .cpp. Pro ty z vás, kteří se ještě nezorientovali, do budoucna se můžeme dostat do situace, že pro projekt budeme mít i více zdrojových kódů (souborů), proto tedy existují projekty - aby nám umožnili pohodlně spravovat zdrojové soubory.
Co "ten program" dělá Ten program je vlastně takový balíček více různých programů. První z nich Vám slouží jako editor a nazývá se vývojové prostředí. Dalším důležitým programem je tzv. compiler, což je program který přeloží program z naší lidské formy do toho, čemu rozumí počítač. Aby toho nebylo málo, je často potřeba více těchto překladů compileru dát dohromady, aby nám z jednotlivých kapitol vznikla celá kniha (např. tedy výsledný spustitelný soubor) - o to se stará linker. Nemusíte se ale všech těchto pojmů bát. Vše lze velmi jednoduše ovládat z vývojového prostředí stiskem několika kláves.
Na závěr prvního dílu Prokousali jsme se tím nezáživným a příště už můžeme začít s vlastním programováním. Napíšeme si jednoduchý program, který nás dovede pozdravit a vysvětlíme si pár základních principů.
Programování v C/C++ - 2. díl A je tu další díl seriálu o programovaní v jazyku C. Minule jsme měli takovou malou seznamovací party, teď už nás ale čeká první rande, vlastně první program.
Náš první program S chutí do toho, hned je hotovo. Založte si nový projekt (typu konzolová aplikace), nějak si ho pojmenujte, pokud vám Dev-C++ už něco vygenerovalo, smažte to. Pokud ne, zkopírujte si následující část kódu. /* tohle je náš první program */ #include<stdio.h> //tady je to důležité int main() { //pozdravíme printf("Ahoj!"); //počkáme na klávesu getchar(); //této řádky si nevšímejte return 0; } Je to takový kraťoučký prográmek, který nás pozdraví, počká až "to odklepneme" a skončí.
Komentáře První řádek začíná dvojicí znaků /*. Tak začíná tzv. komentář, tedy nějaká poznámka, kterou si programátor ke svému programu napsal (tady "tohle je náš první program"). Komentář ale musí i nějak končit. Od toho máme dvojici znaků */. Vše co je mezi /*a*/ je pro kompilátor (compiler) neviditelné, nevšímá si toho. Komentář může mít i více řádek, pokud vám ale stačí řádek jen jeden, můžete psát komentář i za dvé lomítka // (kompilátor si nebude všímat toho co je mezi těmito lomítky a koncem řádku. Není potřeba ho tedy nijak ukončovat, jeho konec je vždy tam, kde končí řádek), jako jsem to udělal o několik řádek níže.
Řádky co začínají # Řádky co začínají # si nebudeme zatím vůbec všímat. Někdy později jim budeme věnovat celou kapitolu.
Vlastní program Vlastní program, tedy náš seznam úkolů pro počítač, je mezi znaménky { a } (složené závorky). To co zbylo před našimi závorkami - main() - si také vysvětlíme v některém pozdějším díle (pro dnešek se budete muset smířit s tím, že pracujme jen a jen s částí mezi složenými závorkami a sem tam nějakým tím komentářem). Nad každým důležitým řádkem jsem napsal, co znamená. Pamatujte si, že jednotlivé instrukce a příkazy pro počítač se od sebe oddělují vždy středníkem. Můžete jich mít na jedné řádce klidně deset (no brrr), ale je důležité je mít oddělené středníkem (jazyk C rozlišuje mezi malými a velkými písmeny, to znamená že printf a PrintF NENÍ to samé!!). Podle toho kompilátor pozná, kde končí jeden příkaz a začíná druhý. Pokud je potřeba k příkazu připojit ještě nějaký parametr, přesněji specifikovat, co se bude dít, je potřeba to uvést v závorkách (příkazy - také se jim říká funkce - po pravdě parametry buď mají, a mají jich přesný počet s přesným významem, nebo nemají (výjímky jsou, ale zabývat se jimi nebudeme). Máme-li tedy, jako ve výše uvedeném příkladu printf("ahoj!"); Znamená to, že máme funkci (příkaz, instrukci), která se jmenuje printf (což znamená "print formated", česky "vytiskni formátované"… funkce mívají často nějaká intuitivní jména) a která vytiskne na obrazovku to, co dostane jako parametr (nějaký text mezi dvojicí uvozovek), v tomto případe bude tedy na obrazovce: Ahoj! Hned jak se náš text vytiskne, provede se funkce getchar (která nemá žádný parametr, takže závorky jsou sice prázdné, ale musejí tam být!), která "přečte" z klávesnice první stisknuté tlačítko (česky "zjisti znak"), tím pádem ale i musí počkat než se zmáčkne :). To je tedy náš první program. Přeložíme ho Ctrl-F10, spustíme Ctrl-F9 (doufám že jste od minule ještě Dev-C++ nesmazali…), chvíli počkáme, než se program přeloží, a mělo by se vám všem objevit okno a v něm Ahoj. Stiskněte klávesu, a program se ukončí. POZN: je potřeba soubor uložit s koncovkou .C, pokud už jste ho ale uložili s koncovkou .CPP a nechce se Vám ho ukládat znovu, stačí přidat před main slovo "int" a před } přidat řádek "return 0;" (vše samozřejmě bez závorek a uvozovek)
Ještě trochu hygieny I Váš kód potřebuje hygienu. Když ho nebudete pořádně udržovat, bude za chvíli zarostlý a ošklivý. To, o čem tu budu mluvit, je důležité. Ač se Vám to teď nezdá, je velmi dobré mít kód přehledný. Snažte se dodržovat nějaký svůj styl. Pokud možno by jeho součástí mělo být, že píšete vždy jeden příkaz na jednu řádku. Pokud to má logicky nějaký smysl, můžete třeba občas vložit prázdnou řádku (jako když píšete nějaký sloh nebo zápis ze schůze - mělo by to být přehledné. Na rozdíl od třeba knihy se ale musíte v tomto textu dobře orientovat na první pohled. Nemůžete ho pokaždé číst od začátku. Dobré a přehledné členění je proto důležité). Dobrým zvykem je také psát za čárku mezeru a někteří ji dávají i za levou závorku a před pravou závorku, to aby věci v závorce byly trochu odsazené (časem zjistíte, že věci v závorkách se daleko častěji mění než např. jména funkcí ;)). Také se nebojte odsazovat (třeba jako v našem příkladu, vše mezi { a } je odsazené (o jeden TAB nebo několik mezer, jak Vám to vyhovuje)). Časem to správné rozmístění dostanete do oka. Podobná věc platí pro komentáře. Nešetřete jimi, pokud budete mít třeba poměr komentář: kód 1:1 (a není naprosto zřejmé, co chcete dělat a proč to chcete dělat), je to výborné. Po několika dnech nebo stovkách řádků třeba zapomenete, co tenhle řádek dělá a hlavně proč to vlastně dělá… hmmmm
Co si zapamatovat Pamatujte si, že jednotlivé funkce/příkazy se od sebe oddělují středníkem (v podstatě jsou to výrazy. Každou funkci nebo příkaz si můžete představit jako funkci v matematickém výrazu. Pokud věcem co jsem do této závorky napsal moc nerozumíte, nic se neděje). Pamatujte si že se komentář píše mezi /* a */ nebo za // a že platí, že čím více, tím lépe. Komentář slouží k tomu, aby jste se ve vlastním kódu lépe vyznali! Pokud má funkce nějaké parametry, píšou se do závorky za jméno funkce/příkazu a oddělují se čárkou. Pokud ne, píšou se tam prázdné závorky. Ne že by vám to bez nich neprošlo, ale znamenalo by to něco jiného :o a to my nechceme.
Programování v C/C++ 3. díl Dnes si budeme povídat o proměnných a o jejich druzích. Konečně už budete schopni napsat program, který bude opravdu něco dělat a v příštím díle si už naprogramujeme malinkatou hru, to jen aby jste měli motivaci do začátku ;-)
Proměnné Proměnnou si můžete představit jako nějakou hodnotu. Tu hodnotu můžete různě měnit, což je velmi důležité, a také z ní můžete číst, což je neméně důležité. Kolem a kolem, v proměnných si uchováváte hodnoty, které si potřebujete ve vašem programu pamatovat. Vlastně naprostou většinu hodnot si je potřeba pamatovat. Pokud by jste psali například úplně jednoduchý program, který se zeptá uživatele na jeho jméno a potom ho pozdraví, je potřeba se uživatele na to jméno zeptat a potom ho tím jménem pozdravit. Pokud bychom si ovšem jméno uživatele nezapamatovali (mi jsme se ho zeptali, ale nezapamatovali jsme si ho!), nebudeme si na něj (na to jméno) moci vzpomenout, až jím budeme chtít pozdravit uživatele. Prostě jsme se zeptali, jedním uchem šlo dovnitř, druhým ven a když má teď váš program pozdravit, připadá si jako po požití návykových látek ;). Proto je potřeba si věci/hodnoty v programu pamatovat (říká se ukládat do proměnných). Pokud by jste si hodnoty nemuseli pamatovat, nemají vlastně pro Vás žádný účel a potom jste je (např. od uživatele) nemuseli ani získat.
Základní typy proměnných Proměnná tedy v sobě uchovává nějakou hodnotu. Takovou hodnotou může být například číslo nebo také text. Prostě, druhy proměnných jsou různé a také se liší v tom, jakým způsobem si je počítač pamatuje (tím se ovšem ještě zabývat nebudeme). V tabulce je uvedeno několik druhů číselných proměnných (proměnnými, které si pamatují text, se budeme zabývat někdy jindy).
Název
rozsah bez znaménka (unsigned)
rozsah se znaménkem (signed nebo samotné)
char
0 až 255
-128 až 127
short short int
0 až 65,535
–32,768 až 32,767
0 až 4,294,967,295
–2,147,483,648 až 2,147,483,647
int long long int
Poznámky
rozsah pro typ int se liší podle platformy (data v tabulce platná pro 32-bitové operační systémy, např. Windows 95/98/NT/XP, Linux atp.)
float
6 číslic pro desetinnou a celočíselnou část dohromady
vždy se znaménkem, ne vždy přesné
double
14 číslic pro desetinnou a celočíselnou část dohromady
vždy se znaménkem, ne vždy přesné
Jak vidíte, liší se tím, jak velká čísla si mohou pamatovat (pokud by jste se do proměnné snažili uložit větší číslo, než je schopna si pamatovat, dojde k tzv. přetečení (anglicky overflow), kdy se proměnná nastaví na hodnotu, která je rovna tomu, o kolik se tam proměnná nevešla (varování: vždy by jste měli vědět jakých hodnot bude proměnný nabývat už když program píšete!). Pokud by jste potřebovali pracovat s ještě větším rozsahem čísel, než jaká jsou tu uvedena, museli by jste použít nějakou pomocnou knihovnu (nic, čím by jste se zrovna teď měli zabývat). Většina jmen proměnných má svůj význam (jak jsme si řekli minule, je důležité rozlišovat mezi malými a velkými písmeny!). Název char je od anglického character - znak (proč? řekněme že z "historických důvodů"), int je od anglického integer - celé číslo, long je long - "dlouhé" číslo, float je float - vznášet se, plout - zde kvůli plovoucí desetiné čárce a konečně double - jako dvojitá přesnost pro desetiná čísla. Potom je tu přívlastek (dává se před typ proměnné) signed a unsigned, což znamená ze se znaménkem a bez znaménka. Je-li číslo se znaménkem, pamatuje si počítač jen poloviční rozsah (místo 256 různých čísel při rozsahu 0-255 je to 256 různých čísel se znaménkem při rozsahu -128 až 127. Slova signed a unsigned jdou "napasovat" před všechny celočíselné typy, desetinné typy jsou vždy se znaménkem. Můžete používat i názvy samotné, pak si k tomu počítač (záleží na nastavení kompilátoru, ale zpravidla) přimyslí signed a z char se stane signed char atp. Příklad použití signed a unsigned unsigned short unsigned short int signed long int unsigned char Stručně zopakováno, máme několik typů celočíselných a desetinných. Liší se v tom, jaký rozsah čísel si pamatují, a u celočíselných v tom, jestli jsou se znaménkem nebo ne.
Jak si "uděláme" takovou proměnnou Pokud potřebujete uložit nějakou tu hodnotu, potřebujete proměnnou. My už jsme si řekli, jaké jsou jejich typy a co si umí "pamatovat". Těď si ukážeme, jak si takovou proměnnou "uděláme" nebo vytvoříme. Řekněme, že chceme uložit číslo v rozsahu 0-10, např. 8. Zvolíme tedy typ char (nebo unsigned char), protože do jeho rozsahu se pohodlně vejdeme (větší rozsahy zabírají více paměti, proto je lepší používat čísla s nejmenším dostačujícím rozsahem). A jak to teď uložíme? Je potřeba zvolit si jméno/název, pod kterým si budeme tu hodnotu ukládat, např. nase_cislo (jména proměnných nesmí začínat číslicí, mohou obsahovat malá a velká písmena, číslice a podtržítko; správná jména jsou např. mojecislo MojeCislo MojeCislo2 Moje_cislo _cislo_ Naopak špatně jsou např. 1_mojecislo moje-cislo moje&cislo moje cislo Dvě proměnné (vůbec dvě jména, tj. jména funkcí, proměnných atp. nemohou mít stejný název. Proto se vám může někdy stát, že vaše jméno už je použito… potom použijte jiné). Do kódu to pak zapíšeme jako //tohle je moje prvni promenna char moje_cislo; Jak jste si už možná všimli, nejprve napíšete typ proměnné, potom její jméno a středník. Další příklady toho, jak vypadá deklarace proměnné jsou signed char my_signed_number; int my_integer_number; double HypergalacticSpeed; float fVelocity; pokud potřebujete více proměnných stejného druhu a nechce se Vám psát typ proměnné znovu a znovu, můžete jednotlivé proměnné (jejich jména) stejného druhu oddělit čárkou a za poslední udělat středník. char prvniCislo, DruheCislo, treti_cislo, CTVRTECISLO; Tomuto (ať už chcete jen jednu nebo více proměnných) se říká deklarace proměnných, tj. Vy deklarujete, že budete používat proměnnou určitého jména a typu a kompilátor se díky tomu může na Váš program "lépe připravit". Než budete proměnnou používat, musíte ji deklarovat.
Kde deklarovat proměnnou? POZNÁMKA: minule byla velká diskuse o tom, jestli se učíme C nebo C++ a co je lepší. Já jsem seriál pojmenoval Programováni v C, protože jsem chtěl dát jasn ě najevo, že se tu nebudeme zabývat objektově orientovaným programováním, ale že se budeme učit základy programování. Pro úplnost tedy - budu se snažit odlišit alternativy jak pro C tak pro C++. Pro Vás, co začínáte, nelamte si hlavu, vyberte si C++, proto že je pohodlnější na práci a bude se vám žít blaze. Žádné změny není třeba podnikat, soubory ukládejte s koncovkou .cpp a máte vystaráno. My si budeme říkat, jak je to pro C++ (pro ty z vás, kteří to chtějí a musejí vědět nebo musí z nějakého důvodu progra movat v C se musí deklarovat proměnné na začátku funkce, hned za levou složenou závorkou) . Proměnnou můžete deklarovat kdekoliv mezi { a } v příkladu z minulého dílu nebo kamkoliv rozumně v programu (zkoušejte to, když to bude špatně, počítač vám to nedovolí) Budu to dělat pořád a pořád: snažte se udržovat si pořádek v kódu, dobře odsazovat (TABem nebo mezerníkem, např. Visual C++ už to dělá automaticky), dnes ještě zdůrazním, aby jste si dobře pojmenovávali proměnné, to znamená žádné bbb, abc, fgh atp, ale hezky celým jménem nebo nějakou zkratkou. Pro inspiraci některé styly, kterými programátoři zapisují proměnné: int iMojePromenna; int moje_promenna; int mojePromenna; int MojePromenna;
Co můžeme s číselnou proměnnou dělat? Je to číslo, můžeme s ním tedy dělat jednoduché operace jako sčítání, odčítání, násobení a dělení. Pokud něco podobného chcete dělat, zapisujete to jako normální rovnici v matematice (středník na konci). Například: a = b + c;
Jako v matematice, a se bude rovnat (do a se přiřadí hodnota) součtu b a c. Z věcí, které by ještě nemusely být úplně jasné: znak pro násobení je *, znak pro dělení je /. Přitom můžete mezi sebou míchat celá čísla i čísla s desetinnou čárkou, převod proběhne automaticky.
Dnešní jednoduchý program /* druhy program! pocitani pokut */ #include<stdio.h> int main() { //tady zacina hlavni blok kodu //tady zacina nas program printf("Vytejte v programu, ktery vypocitava pokuty!\n\r"); printf("Pokuty se budou strhavat ze standardniho platu 15000Kc!\n\r"); //nadeklarujeme 3 promenne typu int int standardni_plat; int snizeny_plat; int pokuta; //nastavime pocatecni hodnoty standardni_plat = 15000; pokuta = 400; //spocitame kolik je snizeny plat; odecteme tedy od promenne standardni //plat, ktera ma hodnotu 15000, promennou pokuta, ktera ma hodnotu 400. //15000-400 je 14600, tzn. ze do promenne snizeny plat se nam ulozi //hodnota 14600 (prace s ostatnimi druhy promennych probiha stejne) snizeny_plat = standardni_plat - pokuta; //vypiseme hodnotu snizeneho platu //POZZ: funkce printf ma promenny pocet parametru, je trochu nestandardni, //ale velmi sikovna printf("Zlobivy zamestnanec si vydela jen %d", snizeny_plat); //pockame na stisk enteru getchar(); //to neni dulezite return 0; } //tady konci hlavni blok kodu
Platnost proměnných Používat proměnnou můžete až potom, co jste jí nadeklarovali. Jenomže proměnné taky mohou "umřít". Celá zápletka je v tom, že proměnná je platná jen v té části kódu, ve které je deklarovaná. Nás zatím tohle umírání nemusí tížit a když na to nezapomenu, zmíním to v příštím díle.
Do příště Zkuste si upravit program tak, aby se standardní plat kvůli mimořádným úspěchům firmy zvednul dvakrát (jinak než tak, že místo 15000 napíšete 30000!). Nápověda: platí stejná pravidla (přednost násobení a dělení před sčítáním a odčítáním) jako u normálních počtů a můžete používat závorky.
Programování v C/C++ 4. díl Dnešní téma je větvení a podmínky. Tu slibovanou hru budeme muset ještě o jeden díl odložit, ale přijde. Minule jsem se tu rozepisoval o proměnných. Doufám že si ještě pamatujete co je to char, short, long, int, double a float. Ani nepředpokládám, že si je pamatujete všechny, ale aspoň polovinu by jste si pamatovat mohli.
Blok kódu Takovým předpokladem pro dnešní téma je vědět, co je to blok kódu. Název není úplně přesný, prostě je to blok, blok kódu, blok programu nebo to mezi složenými závorkami. Představte si, že máte několik papírů. Aby se Vám s nimi lépe pracovalo, procvaknete je sešívačkou. Co je pro nás důležité je, že ta sponka ze sešívačky jednotlivé papíry spojila. Už nepracujete s jednotlivými listy, ale se všemi jako s celkem. Podobně funguje i blok kódu, vezme více řádek a můžete s nimi pracovat jako s jednou. Asi tady budu muset být přesnější. Jestli si ještě vzpomínáte, jak jsem říkal, že každý jednotlivý příkaz nebo funkce se musí oddělovat středníkem. Celý takový blok kódu se pak v programu chová jako jeden příkaz. Takový blok pak může vypadat například nějak takto. { //pozdravime printf("Ahoj, jak se dari"); } Do bloku je možné vkládat libovolně další bloky (což bude potřeba při větvení). Vkládat ale takový blok samoúčelně, pokud nepotřebujete seskupit více příkazů/funkcí, není standardní postup a ani bych to nedoporučoval, ačkoliv to není v zásadě špatně. Taky si v našem dnešním zkušebním programu nebo v ukázkovém programu z druhého dílu všimněte, že tam jeden blok kódu máme (v dnešním příkladu jsem ho označil). Je to takový hlavní blok v programu, na jeho začátku program začíná a na jeho konci program končí.
Větvení programu Může nastat situace (a ona nastane, a mnohokrát), že Váš program se v jeden moment zastaví na křižovatce a bude se muset rozhodnout, jestli odbočí nebo bude pokračovat rovně. Přijde na tu křižovatku, stojí tam a rozhodne se, kterou cestou půjde. To je větvení. Váš program dojde na nějakou svou řádku a tam se rozhodne, co bude dělat. Doufám, že už se ptáte, podle čeho se rozhodne. Rozhodne se podle nějaké podmínky. Například pokud uživatel zadal, že je vyšší než 180 cm, pochválíme ho za jeho výšku. Takovéto jednoduché větvení se dělá pomocí takového kouzelného slovíčka if (v angličtině if = pokud). Podmínku potom napíšeme do závorek hned za tohle kouzelné slovo a to, co se má vykonat hned za to (opět si všimněte, jak kód upravuji, aby se dobře četl) if( vyska > 180) printf("Ty si ale veky clovek"); Výše uvedený úryvek kódu by vzal hodnotu proměnné vyska (která byla samozřejmě někde dříve v programu použita a nastavena) a pokud by ta hodnota byla větší než 180, vypsala by se na obrazovku zpráva. Co když ale chceme provést více věcí než jenom jednu? Jednou možností by bylo napsat jedno if pro každou funkci, kterou chceme vykonat, pokud je podmínka splněna. Tudy ale cesta rozhodně nevede. Cesta naopak vede přes bloky kódu. Názorná ukázka if( vyska > 180 ) { printf("Ty si ale velky clovek…Jestli chces pokracovat, stiskni klavesu"); getchar(); printf("Jedeme dal"); } Zde, pokud je hodnota proměnné vyska vyšší než 180 (pro ty matikou nepoznamenané, > znamená větší než), program opět vypíše gratulaci, počká na stisk klávesy ENTER a informuje nás o tom, že program dál pokračuje. Pokud se podmínka nesplní, to znamená pokud jsme menší než 180 cm nebo máme přesně 180 cm, program pokračuje za funkcí nebo blokem, který následuje za slovem if. Připravil jsem vám dva diagramy, program pokračuje po šipkách.
pokud je hodnota proměnné vyska vyšší než 180
pokud je hodnota proměnné vyska menší než 180 V podmínkách můžete použít několik tzv. operátorů, lidštěji řečeno "značek". Operátor
Komentář
>
levá strana větší než pravá
<
pravá strana větší než levá
>=
levá strana vetší nebo rovna pravé
<=
pravá strana větší nebo rovna levé
== !=
levá strana se rovná pravé (POZOR - jsou to skutečně dvě =, zatímco dvě = porovnávají, jedno = přiřazuje hodnotu! levá strana se nerovná pravé
Říká se, že každý takový operátor má dva operandy. Jinými slovy každá ta značka potřebuje dvě čísla, které bude porovnávat. Když počítač uvidí nějakou takovou podmínku, nejprve si spočítá (vyhodnotí) číslo vlevo a vpravo od operátoru a potom je teprve porovnává. if( 10 - 2 > 3 ) je tedy ekvivalentní if( 8 > 3) Pokud se podmínka, kterou jsem napsali, splní, říká se, že se podmínka vyhodnotila jako pravdivá a nahradí se jedničkou (nenulová hodnota, nejčastěji 1, pro počítač znamená pravda, 0 pro počítač znamená nepravda). Pokud se podmínka nesplní, to znamená že hodnota proměnné výška je menší nebo rovna 180, potom se vyhodnotí jako nepravdivá a nahradí se nulou. Počítač tedy vlastně jenom zajímá, jestli je v závorce za if nula nebo jiné číslo. Pokud je tam nula, znamená to pro něj nepravda, pokud je tam jiné číslo, znamená to pravda a vykoná to, co je za if a závorkou. Pokud bychom měli podmínku, která vypadá zhruba takto if( 10 > 5 ) počítač to "vidí" jako if(1) Přeloženo do češtiny: Pokud je to pravda (…něco udělej…). Určitě vám hodně pomůže, když si budete takhle podmínky číst. (nahrazení jedničkou nebo nulou probíhá samozřejmě až za běhu programu, protože v době, kdy program píšete ještě neznáte hodnotu proměnné, která se objeví v podmínce) Pokud, v zájmu například lepší srozumitelnosti kódu, nebo i z jiných důvodu, potřebujete hodnotu otočit (udělat z pravdy nepravdu nebo z nepravdy pravdu), potom stačí před hodnotu, kterou chcete otočit, napsat vykřičník. if(!(18 > 15)) Což by znamenalo "pokud 18 není větší než 15". Vyhodnotilo by se to na: if(!(1))
Vykřičník nám pravdu (jedničku) otočí na nepravdu (nulu). if(0) A máme to.
Co když potřebuji více podmínek: Pokud je potřeba, aby bylo splněno více podmínek, můžete do závorek za if uvést podmínek i více. Mezi jednotlivými podmínkami je potřeba určit vztahy. Pokud mají být splněny obě podmínky, napíšete mezi ně &&, což se čte jako "a zároveň". Pokud vám stačí, aby byla splněna alespoň jedna podmínka, napíšete mezi ně || (dvě svislítka), což se čte jako "nebo". if(stari >= 17 && stari <= 25) printf("Chlap mezi 17 a 25, to je presne to co hledam :) "); "nebo" mezi podmínkami if(pocet_nohou > 2 || pocet_rukou > 2) printf("Ty asi nebudes clovek, ze ne?"); &&
A ZÁROVEŇ
||
NEBO
Pokud je levá i pravá strana pravdivá pokud je levá nebo pravá strana pravdivá, nebo jsou pravdivé obě
Ještě jednou zdůrazním, že u "nebo" stačí jen jedna ze dvou podmínek pravdivá, ale mohou být pravdivé i obě!). Pokud máte více podmínek než dvě, obzvláště pokud ještě k tomu kombinujete && a ||, dávejte velký pozor na to, v jakém pořadí se vyhodnocují a radši si vždy pomozte dalšími závorkami. Jednotlivé || a && se vyhodnotí opět jako pravdivé nebo nepravdivé a z if((1 && 1) || 0) se stane (vyhodnotí se nejprve závorka, 1 && 1, tedy "pravda a zároveň pravda", což je pravda, nebo-li pro počítač 1). if(1 || 0) z čehož se dále stane (alespoň jedno z dvojice pravda a lež je pravda, což je pravda, tedy 1) if(1) Pokud jste tím vším trochu zmateni, nic si z toho nedělejte. Pokud budete schopni lidskou větu správně přeformulovat na počítačovou podmínku, jste za vodou.
Další kouzelné slovo: else Pokud chcete, aby se, pokud je podmínka pravdivá, něco vykonalo a pokud ne, vykonalo se něco jiného, nemusíte použít dvě "opačné" podmínky. Stačí za funkci nebo blok, který máte za vaším if, dát slovíčko else a za něj hned funkci nebo blok, který nebo která udělá to, co chcete udělat, pokud je podmínka nepravdivá if(vek >= 18) //vykona se pokud je podminka spnena printf("Plnolety(a)"); else //vykona se pokud je podminka nesplnena pritnf("Neplnolety");
Zkušební program pokud si chcete osahat práci s podmínkami více, upravujte si podmínky v následujícím programu. #include<stdio.h> int main() //tady zacina nas hlavni blok! { /* tuhle podminku si muzete zkusit upravovat. zkousejte menit hodnoty a operator a zkuste také slozitejsi podminky s && a || */ if(10 > 5) printf("podminka splnena"); else printf("podminka nesplnena"); getchar();
//toho si nevsimejte return 0; } //tady konci hlavni blok
Dodatek o platnosti proměnných Jak jsem slíbil v minulém díle, zmíním se tu ještě krátce o platnosti proměnných. Proměnná platí do konce bloku, ve kterém je deklarována, ale platí i v blocích vnořených v jejím bloku
Programování v C/C++ 1. bonus První bonusový díl je tady a s ním naše první hra. I když ještě nejsme schopní napsat celý program úplně sami, určitě budeme schopni ho napsat s trochou nápovědy.
Pravidla Naše hra bude hodně jednoduchá: hádání čísla. Doufám, že teď nejste zklamaní, ale pořád jsme jen začátečníci, že? Pravidla jsou jednoduchá. Počítač si myslí náhodné číslo od 1 do 100 a uživatel hádá, jaké to je. Pokud uhádne, počítač mu pogratuluje a program se ukončí. Pokud uživatel hádal číslo menší, řekneme mu, že hádané číslo je větší. Naopak pokud bude hádat větší, upozorníme ho, že hádané číslo je menší.
Jak na to? Ještě než začneme, položím vám otázku: jak by asi měl program podle vás vypadat, jakou by měl mít strukturu (jaké konstrukce/smyčky by jste použili, jaké proměnné a co budou vyjadřovat)? Zkuste si na tuto otázku odpovědět, klidně si dejte i chvilku na rozmyšlenou.
Plán Já jsem se rozhodl pro 3 proměnné. Jednu, která bude uchovávat naše hádané číslo, jednu, která si bude pamatovat, co uživatel zrovna tipoval za číslo a jednu, která nám bude pomáhat s hlavní smyčkou v programu. //do teto promenne ulozime cislo, ktere si pocitac vymysli int hadane_cislo; //do teto promenne ulozime cislo, ktere uzivatel zadal int uzivatel_zadal; //ma program pokracovat nebo uz uzivatel uhadl? int pokracuj; Co se týče smyček, rozhodl jsem se pro jednu hlavní while smyčku. Váš návrh může vypadat samozřejmě jinak, dovedu si například představit i řešení s do…while smyčkou. Nic Vám nebrání si to zkusit napsat podle svého. To samé platí i pro vaše proměnné. V podmínce této mé smyčky se bude vyhodnocovat proměnná pokracuj. Pokud bude mít hodnotu 1, bude se dál pokračovat, pokud bude mít hodnotu nula, smyčka se sama ukončí. while(pokracuj == 1) { //tady bude vlastni kod }
Příprava na smyčku Ještě než ale začneme se smyčkou, je potřeba nastavit naše proměnné na správné hodnoty. Například pokracuj musíme nastavit na 1, protože jinak by se nám nevykonal ani jeden cyklus naší smyčky. Potom je potřeba ještě do hadane_cislo uložit nějaké náhodné číslo… o to se postarají dvě následující řádky //trocha magie, tohle jeste neumime srand((unsigned)time(NULL)); //do hadane_cislo ulozime pocitacem vygenerovane nahodne cislo //teto radce take jeste asi nerozumite hadane_cislo = rand()%100 + 1;
Smyčka Ve vlastní smyčce potom vždy přečteme číslo od uživatele //tato radka precte od uzivatele cislo //zatim budeme predpokladat, ze uzivatel skutecne //zada cislo a nebude se snazit vnutit nam nejaka pismenka scanf("%d", &uzivatel_zadal); a třemi if-y potom zjistíme, jestli je číslo menší, větší, nebo jestli uživatel uhádl. Zkuste si program opravdu napsat sami, pasivní znalost je poloviční znalost.
Kód Zde příkládám funkční kód této hry. Pokud chcete hru trochu vylepšít, zkuste ji upravit tak, aby měl uživatel na uhádnutí čísla jen několik pokusů. #include<stdio.h> #include<stdlib.h>
#include
int main() { //do teto promenne ulozime cislo, ktere si pocitac vymysli int hadane_cislo; //do teto promenne ulozime cislo, ktere uzivatel zadal int uzivatel_zadal; //ma program pokracovat nebo uz uzivatel uhadl? int pokracuj; //nastavime nase promenne pokracuj = 1; //trocha magie, tohle jeste neumime srand((unsigned)time(NULL)); //do hadane_cislo ulozime pocitacem vygenerovane nahodne cislo //teto radce take jeste asi nerozumite hadane_cislo = rand()%100 + 1; //dokud je pokacuje rovno 1 while(pokracuj == 1) { //ptame se uzivatele na cislo mezi 1 a 100 printf("hadej cislo od 1 do 100\n\r"); //tato radka precte od uzivatele cislo //zatim budeme predpokladat, ze uzivatel skutecne //zada cislo a nebude se snazit vnutit nam nejaka pismenka scanf("%d", &uzivatel_zadal); //pokud uzivatelem zadane cislo je vetsi nez to, ktere //si mysli pocitac if(uzivatel_zadal > hadane_cislo) printf("kdepak, hadane cislo je mensi…\n\r"); //pokud uzivatelem zadane cislo je mensi nez to, ktere //si mysli pocitac if(uzivatel_zadal < hadane_cislo) printf("kdepak, hadane cislo je vetsi…\n\r"); //pokud uzivatele zadane cislo je rovno cislu, ktere //si mysli pocitac if(uzivatel_zadal == hadane_cislo) { //pogratulujeme printf("Uhadl jste!\n\r"); //nastavimje pokracuj na 0, to ze while-smycka //se, hned jak se dojde zpet k while, ukonci pokracuj = 0; } } //rozloucime se printf("nashledanou"); //pockame na ENTER getchar(); //tajemna radka return 0; }
Programování v C/C++ 5. díl Začneme už trochu tradičně opakováním. Naposledy jsme se seznámili s větvením programu a podmínkami. Doufám, že jste vše náležitě pochopili, protože dnes na předešlé téma navážeme. Co je podmínka, a jak se zapisuje, už jsme se naučili a dneska si probereme ty zbylá kouzelná slova, co nám pomáhají řídit, kudy se program ubírá.
WHILE While je dneska první na řadě. Zapisuje se takhle while(podmínka) příkaz; A říká počítači, že dokud je podmínka v závorce pravdivá, má vykonávat příkaz, který je za závorkou uveden. Pokud jste pozorně četli minulé díly, tušíte správně, že příkaz můžete samozřejmě nahradit i blokem kódu (to, že se může příkaz nahradit blokem kódu, už budeme brát za samozřejmé a nebudeme to znovu zmiňovat). V praxi while (anglicky „dokud (je pravda)“) použijete velmi často. Program totiž často dělá něco dokola a dokola. Program počká, co po něm uživatel chce, provede to a znovu čeká. To se děje právě v tomto while . Mimochodem, často se také říká smyčka, což značí právě to, že vždy, když se program dostane na konec bloku, vrátí se zase na jeho začátek (pokud je splněna podmínka; v naprosté většině případů se za while používá blok kódu). Praktickým příkladem pro while je následující úryvek kódu: int cislo; cislo = 0; //program o sobe neco napise printf(“pocitani do 99\n\r”); //dokud je hodnota promenne cislo mensi nez 100, vykonej blok kodu while( cislo < 100) { //zvetsime cislo o 1 cislo = cislo + 1; //vypiseme ho printf(“%d \n\r”, cislo); } Program deklaruje proměnnou cislo, nastaví ji na nulu a potom začne smyčka. V ní se nejprve vyhodnotí podmínka. Pokud je pravdivá, přejde program do bloku kódu a k proměnné cislo přičte 1, a vypíše se. Potom se program vrátí zpátky na řádku s while a začne se opět porovnáním podmínky. Všimněte si, že pokud je podmínka nepravdivá, nemusí se vykonat ani jeden cyklus smyčky. Pokud je podmínka splněna, vykoná se smyčka, pokud není, nevykoná a program pokračuje za ní.
Variace na while Takovou sestřičkou while je tzv. do…while . Chová se naprosto stejně jako while, akorát podmínka se vyhodnotí až po provedení smyčky, nikoliv na jejím začátku. Zapisuje se: do příkaz while(podmínka); Na rozdíl od samotného while je tady navíc slovo do (anglicky „dělej“) a while je až na konci. Nejprve se vykoná blok kódu. Potom se vyhodnotí podmínka. Je-li pravdivá, celé se to opakuje. Pokud nepravdivá, program pokračuje normálně dál. Žádná velká věda.
Smyčka for Občas potřebujete něco udělat přesně několikrát. Třeba třikrát pípnout. Potom se používá tzv. for-cyklus, který se zapisuje for(nastavení proměnné; podmínka; co se s proměnnou v každém cyklu provede) příkaz; Ano, v závorce jsou tři části oddělené dvěma středníky. Je to o trochu složitější, než bylo while, takže rovnou uvedu praktický příklad. int i; for(i = 0;i < 3;i = i + 1)
{ //fiktivni funkce ktera pipne pipnout_prosim(); } Tento kód ze všeho nejdřív nastaví proměnnou i na 0. Potom se vyhodnotí podmínka. Pokud je pravdivá (tedy i je menší než tři), provede se blok kódů (zde bych ani blok kódu použít nemusel, ale udělal jsem to, aby byl kód lépe čitelný). Na konci takového bloku kódu se provede poslední část závorky, tedy i = i + 1. Jinými slovy: i se zvýší o jedu. Je samozřejmě na Vás, jaké počáteční hodnoty zvolíte a jestli budete přičítat jedničku nebo například odečítat trojku. Nakonec se počítač vrátí zpátky k for, zjistí, jestli je podmínka stále pravdivá a znova se začne vykonávat blok kódu. Určitě si někteří z vás všimli, že to samé můžeme zapsat i pomocí while . Ano, můžeme (pod odstavcem jsem to udělal, aby jste se měli čeho chytnout). Ale for-cyklus se používá, protože pak každý hned pozná už z prvního řádku (protože tam je ta důležitá závorka), co taková smyčka dělá a nemusí hledat v bloku kódu, kde se která proměnná mění atp. int i; //i musime nastavit na nulu i = 0; //pokud je i mensi nez 3 while(i < 3) { //fiktivni funkce, ktera zaridi pipnuti pipnout_prosim(); //k i pricteme 1 i = i + 1; } Proměnné (v tomto případě i; většinou se používá, potřebujete-li do sebe vnořit více for-cyklů, proměnných s názvy i, j, k atd., ale můžete je s klidným svědomím nazvat, jak chcete), která počítá počet cyklů, (kolikrát se vykonal blok kódu za závorkou) se říká čítač.
Učíme se číst Hodně vám pomůže, když se naučíte „číst“ tyto konstrukce. Zkuste si to trochu osvojit, obzvláště pokud máte s touto látkou problémy. Jako bonus i pár řádek k minulé hodině. while (i > 3) { //nejaky kod }
dokud je i větší než tři, vykonávej blok kódu
while (teplota_vody <= 100) { //nejaky kod }
dokud je teplota_vody menší nebo rovna stu …
do { //nejaky kod } while (i >= 100 && i =< 200)
vykonávej blok kódu dokud i je větší nebo rovno stu a zároveň i je menší nebo rovno dvěma stům
for (i = 0;i < 25;i = i + 1)
pro i rovno nule přičítej v každém cyklu jedna, dokud i je menší než 25
if (!(cislo == 0))
pokud neplatí (to je ten !), že se cislo rovná nule
if (cislo < 18 || cislo == 25)
pokud je cislo menší než 18 nebo je cislo rovno 25
Shrnutí Teď už víme, co je to while, do…while a for-cyklus. Všechno jsou to stále důležité věci, takže experimentujte. S dotazy se, jako obvykle, můžete obracet do diskuse pod článkem. Slibovanou hru si napíšeme v bonusovém díle, který by vychází společně s tímto.
Programování v C/C++ 6. díl Po trochu delší odmlce je tu opět další díl našeho seriálu. Doufám, že si ještě něco pamatujete a že jste případně sami trochu pokročili. Každopádně naším dnešním cílem je povědět si něco víc o tom, jak ještě fungují funkce a kde všude se dají použít.
Vyhodnocování výrazů Vezměte si například, takovou matematickou funkci abs (tedy absolute - je to funkce, která vrátí absolutní hodnotu z nějakého celého čísla, jednoduše pokud je číslo záporné, změní jeho znaménko na plus, pokud už je kladné, nedělá nic). Pokud bychom měli nějakou proměnnou a chtěli si do ní uložit právě absolutní hodnotu, dá se to, jak už všichni nejspíš víte, udělat asi takhle: //promenna cislo, do ktere se bude ukladat nase obsolutni hodnota int cislo; cislo = abs(-5000); Je jasné, že do proměnné číslo se uloží hodnota 5000. I když si myslím, že už to většina z vás okoukala, přeci jenom to ještě trochu dovysvětlím. Program běží a dostane se na naši řádku s funkcí abs. Protože do proměnné cislo se má uložit hodnota z "druhé strany rovná se", tj. výsledek funkce abs, vyhodnotí se, jakou hodnotu vrací funkce abs s daným parametrem a ten se uloží. To je samo o sobě docela zřejmé a chápatelné. Z toho ale také vyplývá, že místo čísla můžete kdekoliv použít i nějakou funkci, která číslo toho druhu vrací. Můžete je tedy například použít i u podmínek (pokud si budete chtít kód otestovat, na začátek programu přidejte ještě řádku #include ) int hodnota; hodnota = -30; //ne zrovna ukazkova podminka na zjisteni toho, jestli //je cislo kladne nebo zaporne if(hodnota == abs(hodnota)) { printf("cislo je kladné!"); } To platí samozřejmě i například u while a jiných. Nebojte se experimentovat. Funkci můžete použít i jako parametr jiné funkce, opět je ale potřeba dbát na to, aby byla zachována přehlednost kódu. Pokud bude váš kód vypadat takhle: a(b(), c(), d(e(), 10), f()); moc z něj asi nevyčtete. Navíc si dávejte pozor, aby funkce, které dáváte jako parametr, nebyly extrémně paměťově náročné (snad se o tom zmíním někdy později, ale bývají to jen vyjimečné případy). Taky si všimněte, že když je funkce takhle někde "zabudovaná", nepíšeme za ni středník. Jestli si pamatujete, mluvili jsme o tom, že středník odděluje jednotlivé funkce/příkazy. Tady ale chápejte celou tu konstrukci (ať už je to funkce co má za parametr nějakou další funkci nebo podmínka nebo něco jiného) jako jeden příkaz.
Líné vyhodnocování Má to ale ještě jeden háček. V C/C++ se totiž uplatňuje tzv. líné vyhodnocování. Jakmile je jasné, že podmínka nemůže být splněna, přestane její vyhodnocovaní. Například při if(abs(0) && abs(-1)) je hned po vyhodnocení abs(0) jasné, že podmínka nemůže být splněna (&& - AND - a zároveň… obě strany musí být pravdivé, abs(0) je rovno 0, tj. nepravda, z toho plyne, že podmínka nemůže být splněna). Vyhodnocování tedy skončí a abs(-1) se už neprovede! To se vám může stát osudným, pokud by jste ve funkci, která je v podmínce, vykonávali ještě nějakou důležitou činnost, např. if(NahrajZvuky() && NahrajGrafiku()) { printf("zvuky i hudba v poradku nahrany"); } Zde by stačilo, aby chyběly zvukové soubory (které ale třeba nejsou k běhu hry/programu vůbec potřeba a uživatel si je nestáhl) a nenahraje se ani grafika (která už je k běhu programu většinou potřeba). Dávejte si na to pozor. To samé platí i u ||, jakmile je jedna část podmínky pravdivá, je jasné, že je podmínka splněna a druhá část se nevykoná/nevyhodnocuje. Těchto vlastností můžete využít ve svůj prospěch, ale můžou vás i stát hodně nervů. Pokud to vyřešíte jiným způsobem, nikdo vám to nebude mít za zlé. Navíc by to opět udělalo váš program čitelnější. Pokud se tedy
rozhodnete nějaké podobné vlastnosti použít, nezapomeňte se o tom zmínit v komentářích, tohle není úplně běžně používaná konstrukce.
Funkce Po takovém opakovacím a upozorňovacím začátku bych se rád pustil do funkcí. Když si představíte, že celý Váš program (zrovna například píšete třeba průlomový operační systém) je napsaný tak, jako ho píšeme doteď …. int main() { //tady tisíce různých řádek return 0; } měli by jste se zhrozit. Samozřejmě spousta začátečníků píše programy podobně "v kuse". To se hodí pro krátké utilitky nebo jiné menší programy, ale rozhodně už ne ani pro většinu normálních aplikací. Program totiž můžeme šikovně rozdělit na spoustu menších dílů (funkcí/procedur). Každá funkce se potom stará o nějakou specifickou činnost a když potřebujeme takovou činnost vykonat, prostě "zavoláme" příslušnou funkci a ona to vykoná za nás. Tady bych rád podotknul, že s jazykem C/C++ je "přibalena" už i spousta předpřipravených funkcí. Z těch, se kterými jsme se už seznámili to jsou např. printf, getch nebo o pár odstavců výše uvedené abs. Je mnoho případů, kdy se hodí kus programu "vyříznout" a dát do funkce. Třeba když některou činnost v programu často opakujete nebo když se jedná o rozsáhlý kus kódu, který by nemusel působit přehledně (i když je v celém programu jen jednou) a vám by se hodilo celou tu velkou hrůzu někam schovat. Typickým příkladem nějaká rozsáhlá inicializace programu, kde se nahrává spousta věcí ze souboru atp. Je lepší to schovat do nějaké funkce a pak jen zavolat nahraj_vse_potrebne_a_nastav_promenne(); Pro často opakující se kus kódu by příkladem mohla být například naše matematická funkce abs. Ta může být potřeba v programu na různých místech nesčetněkrát (tedy v nějakém matematickém programu). Protože už jste s funkcemi pracovali (volali jste je když jste potřebovali počkat na stisk klávesy nebo něco vypsat na obrazovku), měli by jste vědět, že funkcím lze dávat parametry a taky že nám funkce mohou vracet hodnotu.
V praxi Dost teorie, ukážeme si konečně, jak se taková funkce definuje (definuje znamená, že píšeme, co má funkce dělat) typ_navratove_hodnoty jmenofunkce(typ_parametru jmeno_parametru, typ_parametru jmeno_parametru, …., typ_parametru jmeno_parametru) { //tady je vlastni kod funkce } pozn: ano, poznali jste, main je také funkce… je to funkce, která se pustí po začátku programu a chová se v mnoha věcech trochu podivně Typ návratové hodnoty je prostě to, co chcete vracet. Může to být float, int, char nebo jiné typy, se kterými jsme se už seznámili. Pokud nechcete vracet hodnotu, potom je návratový typ void (anglicky prázdnota). Následuje jméno funkce, například tedy MojePrimovaFunkce (jméno se řídí stejnými pravidly jako jména proměnných). Za ní obyčejné závorky () a v nich jednotlivé paramtery oddělené čárkou (pokud žádné parametry nechcete, jsou to jen prázdné závorky; u parametru je vždy nejprve jeho typ a pak jeho jméno) například: void ahoj(); int SpocitejVekVeDnech(char den_narozeni, char mesic_narozeni, int rok_narozeni); char je_cislo_mocinou_dvojky(int cislo); Určitě vás už zajímá, jak se ve funkci přistupuje k parametrům. Úplně stejně, jako by to byla proměnná (která má ale platnost jen uvnitř funkce, ona to také je proměnná… kompilátor za vás totiž vytvoří pro každý parametr odpovídající proměnnou a do ní uloží hodnotu, která byla funkci předána). Z toho vyplývá, že cokoliv pak s parametrem/proměnnou uděláte, se projeví jen uvnitř funkce Takováto funkce by určitě nefungovala void pricti_jednicku(int cislo) { cislo = cislo + 1; }
Protože jak jsme si řekli, ve funkci pricti_jednicku se vytvoří proměnná cislo a k té se přiřadí hodnota 1. Zhruba takhle … { cislo = 1; cislo = cislo + 1; } Můžete ale hodnotu z funkce vrátit. Nejprve musíme uvést, že budeme hodnotu vracet int pricti_jednicku(int cislo) a pak ve vlastním bloku funkce (říká se těle funkce, tedy ve vlastním těle funkce) příčteme k proměnné cislo jedničku a vrátíme. To se dělá pomocí slova return a za ním napsané hodnoty nebo proměnné. Slovem return (anglicky návrat) se ale funkce ukončí (návrat k hlavnímu programu). Opět, int pricti_jednicku(int cislo) { cislo = cislo + 1; return cislo; } V programu potom { int moje_promenna; moje_promenna = 1; //pricteme k moje_promenna jednicku moje_promenna = pricti_jednicku(1); //ted by se melo vypsat na obrazovku cislo 2 printf("%d", moje_promenna); } Už je toho celkem hodně. Dnes už na vás nebudu více nakládat. Jen si pamatujte, že parametr se zkopíruje/přiřadí do proměnné, kterou pro vás připraví kompilátor a s tou vy pak pracujete (jak to obejít si někdy ukážeme; ve funkci můžete přistupovat ke globálním proměnným (to jsou proměnné deklarované mimo tělo nějaké funkce - platí totiž v celém programu), ale není to vždy zcela dobrý přístup, obzvláště opět kvůli přehlednosti při používání ve více funkcích najednou). Dále že návratová hodnota se vrací před slůvko return a že se tak také ukončí funkce (pozn. když je návratový typ void, tedy nic, můžete funkci také ukončit předáním "žádné" návratové hodnoty: samotným return). Pokud si chcete látku trochu ozkoušet: Napiště funkci, která bude vracet absolutní hodnotu z čísla předaného v parametru řešení (funkce + vlastní program): #include #include #include int absolutni_hodnota(int cislo) { //pokud je cislo zaporne, vratime -cislo, tedy cislo * -1 if(cislo < 0) return -cislo; else return cislo; } int main() { int promenna; promenna = absolutni_hodnota(-5); printf("%d", promenna);
getch(); return 0; }
Programování v C/C++ 7. díl Po dlouhé době opět další díl (chtěl bych poděkovat především všem těm, kteří mne svými reakcemi přesvědčili seriál obnovit). Minule jsme si řekli jak vypadá funkce a něco o vyhodnocování výrazů. Pokud už si z toho něco nepamatujete, nebojte se zkonzultovat minulý díl. Lidé zapomínají, od toho je nápověda.
Dnešní téma Úkolem bude vysvětlit si, co je to preprocesor a k čemu a jak se využívá. V druhé půlce si představíme některé dosud neznáme operátory (znaménka).
Preprocesor a jeho direktivy V nějakém vašem programu jste už určitě narazily na řádky, které začínají #. Váš editor je odlišuje barevně nebo třeba tučnějším písmem, a já jsem vám zatím jejich výraz zatajil. Nazývají se direktivy preprocesoru. To je sice krkolomný název, ale nikdo vás přece nenutí si ho pamatovat. Jde o to, že ještě předtím, než přijde řada na samotnou kompilaci (kdy se tedy z vašeho .cpp souboru stává .exe), vezme počítač váš zdrojový kód a nechá ho předzpracovat právě preprocesorem (pre = před, procesor = "zpracovávač"). Ten podle toho, co jste za # napsali, s vaším programem něco provede.
#include<soubor.h> Vezme soubor s jménem, které jste uvedli mezi < a > a celý ho vloží na místo, kde je vaše #include< >. Tento soubor pak obsahuje zdrojový kód pro některé předpřipravené funkce, které můžete ve vašem programu použít. Často jsme vkládali soubory stdio.h, stdlib.h a conio.h.Právě ty obsahují některé funkce jako printf, getch atp.
#include"soubor.h" Liší se od #include< > jenom v tom, odkud soubory bere. #include< > je bere z adresáře, který je přednastavený ve vašem vývojovém prostředí, zatímco #include" " je bere z aktuálního adresáře. Zatím tuhle direktivu nebudete potřebovat, ale může se stát, že na ni narazíte.
#define JMENO HODNOTA Direktiva, se kterou budete pracovat asi velmi často. Preprocesor nahradí výskyt všech JMENO ve zdrojovém kódu slovem HODNOTA. To se používá, pokud máte nějakou hodnotu, která se v kódu často vyskytuje. Příkladem by bylo třeba řecké PI. #define PI 3.1415 … float vysledek; vysledek = sin(PI); … Hlavním účelem je, aby byl text přehledný. Obzvláště když píšete matematické programy nebo potřebujete počítat něco podle vzorce, můžete se dostat časem do situace, kdy už nevíte, co tohle nebo tamto číslo vlastně má znamenat a proč jste ho tam dali. Takhle ho můžete krásně pojmenovat. Dalším, ještě důležitějším účelem je potom to, že změnou jednoho řádku s #define změníte snadno všechny výskyty na jinou hodnotu. Například #define PRACOVNIKU 10 int rozpocet_na_platy; int i; rozpocet_na_platy = 100000; for(i = 0;i < PRACOVNIKU;i++) { … } printf("kazdy pracovnik dostane %d Kc", rozpocet_na_platy / PRACOVNIKU); Teď si představte, že potrebujete změnit ve vašem výpočtu počet pracovníků. Není nic snažšího, než místo #define PRACOVNIKU 10 napsat #define PRACOVNIKU 25 Můžete sice namítnout, že můžeme počet pracovníků uložit do proměnné, což je naprostá pravda. Ale jsou i případy, kdy se to nehodí (zmíním se o tom v některém z podějších dílů).
Další direktivy mezi další direktivy, na které můžete narazit, patří například jiné formy #define, #ifdef, #pragma apod. To jsou všechno věci, bez kterých se zatím rozhodně obejdete.
Některé další operátory Když jsme mluvily o operátorech jako +, -, * a /, vynechali jsme ještě některé jejich mutace (tohle už nejsou znaménka jak je známe z matiky, ale trochu nestandardní znaménka z programování… budeme jim prostě říkat nadále jen operátory). Kolikrát už jsme potřebovali přičíst k proměnné nějaké číslo, třeba 5? Zapisovali jsme to vždy jako char promenna; promenna = promenna + 5; Dá se to ale zapsat i daleko pěkněji. Potřebujete-li tedy některou proměnnou vynásobit, vydělit, zvětšit nebo zmenšit o nějakou hodnotu nebo nějakou hodnotou, můžete na to použít operátory z tabulky. Ty tu nejsou od toho, aby nám dodali nějakou novou funkčnost, ale aby nám usnadnili zápis našeho programu. nový operátor a += b a -= b a *= b a /= b
jinak zapsáno a=a+b a=a-b a=a*b a=a/b
čte se plus-rovná se; zvětšeno mínus-rovná se; zmenšeno krát-rovná se děleno-rovná se
Názvy jsou spíše nápověda, můžete je slyšet i pod jiným jménem. Tyto operátory se běžně používají a můžete je bez bázně a hany používat pořád (popravdě je to lepší, než psát delší formu, opět to totiž zvyšuje čitelnost programu).
Operátory -- a ++ Pokud potřebujete proměnnou zvětšit nebo zmenšit o jedna, můžete k tomu použít operátory ++ a -- (nic obdobného pro * a / neexistuje). a = 1; a++; printf("%d", a); //tady se vytiskne 2 Má to ale jeden háček, můžete totiž napsat jak a++; tak ++a; Rozdíl nastane, když by jste napsali (což už je celkem složité) a = 0; promenna = a++; a a = 0; promenna = ++a; V prvním případě se totiž promenna bude rovnat 0, v druhém 1. a++ totiž přičte k a číslo 1, ale "vrátí"-nahradí se hodnotou původního a. U ++a je to naopak. Také se zvětší o jedna, ale je nahrazeno už o jedna zvětšeným číslem místo čísla původního. Tedy Napíšeme-li promenna = a++, je to stejné, jako bychom napsali promenna = a; a += 1; Napíšeme-li promenna = a--, je to jako a += 1; promenna = a; Já osobně používám ++ a -- jen pokud potřebuji proměnnou zvětšit nebo zmenšit, ale ničemu ji nepřiřazuji, například for cykly jako int i; for(i = 0; i < cislo; i++) { …
} a podobně. Nedoporučuji (a to celkem silně) vám používání operátoru ++ a --, když chcete něčemu přiřazovat hodnotu, hlavně opět kvůli přehlednosti (a aby jste pak nehledali dlouho chybu, kolikrát už jsem kvůli vlastní lenosti strávil hodiny hledáním chyb). Může se vám stát, že to někde uvidíte.
Program Napiště program, který pro každého druhého pracovníka z 20 vypíše jeho číslo (nápověda: vypsat každého druhého vlastně znamená posouvat se při procházení o dva) #include<stdlib.h> #include<stdio.h> #include #define PRACOVNIKU 20 void main() { int i; for(i = 0;i < PRACOVNIKU;i += 2) { printf("%d\n", i); } }
Příště Přijdou na řadu tzv. pointery. To je jedna z nejdůležitějších věci (vše co bereme je důležité, ale pointery obzvláště) a bude potřeba jít malinko do nitra počítače a vysvětlit si, jak to funguje s pamětí.
Programování v C/C++ 8. díl Dnes načneme téma, které je nadmíru důležité a rozdělíme ho dokonce na několik článků. Tím tématem jsou ukazatele, nebo anglicky pointery (oba výrazy se používají).
Pointery Jak vypadá paměť Nejprve si řekneme, jak zhruba vypadá paměť v počítači (RAM). Ono nám ani tak nejde o to, jak ve skutečnosti vypadá, ale jak s ní pracujeme. Pro naše účely je tedy paměť spousta "políček" nebo "buněk", které jdou za sebou. Každé takové políčko je jeden byte [bajt]. Když to tedy shrneme, spousta políček o velikosti jednoho bytu za sebou. Co je to byte[bajt]: jednotka počítačové paměti. Skládá se z osmi bitu [bit], přičemž každý bit může být buď 1 nebo 0 (to jsou právě ty často zmiňované jedničky a nuly). Na úrovni bitů my ale pracovat nebudeme, budeme pracovat s byty. Každé to políčko má svoje číslo. Takovému číslu se říká adresa, nebo delším názvem adresa v paměti. Jako kdyby každá buňka paměti byl jeden obyvatel velkého sídliště :-).
Proměnné a paměť Každá proměnná musí být samozřejmě někde uložená v paměti. Mohli by jste se ptát, kolik té paměti nám sebere. Konkrétní velikosti jednotlivých druhů proměnných uvádím pro orientaci v tabulce. Nejsou ani tak důležíté, důležité je si uvědomit, že hodně z nich má přes 1 bajt. typ proměnné její velikost v bytech char
1
short, short int int, long, long int
2 4
float
4
double
8
podle operačního systému se velikost proměnných v bytech může lišit. Tato tabulka platí pro 32-bitové operační systémy jako Windows 95/98/NT/2000/Me a podobné, Linux a jiné Unixové sýstémy. Neplatí to ale například vždy v DOSu, kde jsou některé velikosti menší.
Stack a heap Kvůli některým vlastnostem pointerů je potřeba povědět si, co je to stack (tzv. zásobník) a co je to heap (tzv. halda). Já budu používat v článku anglickou terminologii, ale běžně se používá oboje. Obecně pro stack i pro heap platí, že jsou to místa v paměti, lépe řečeno kusy paměti, které jsou vyhrazené pro nějaký specifický účel a podle toho se odvíjí i jejich vlastnosti. Stack je kus paměti, který se vytvoří při startu aplikace (tedy ono je to trochu jinak, ale v zásadě). Ukládájí se na něj všechny proměnné, které ve funkci deklarujete (kromě globálních). Když jsme se bavili o parametrech funkcí, říkali jsme si, že se v těle proměnné vytvoří duplikáty proměnných, které jsme předali jako parametr. Ty se ve skutečnosti, stejně jako všechny proměnné vytvořené v těle funkce, přidají na stack. Když pak z funkce vyskočíte, zase se ze stacku umažou. S heapem už je to jednodušší. To je ta zbylá část paměti, kterou můžeme používat (sou i místa v paměti, které používat nemůžeme). Stack a heap se nepřekrývají, ale platí pro ně to stejné číslování adres. To znamená, že jsou vlastně v paměti na políčkách umístěny za sebou, ale jeden nemůže přerůst do druhého. V pravdě bývají jejich velikosti většinou pevné.
ilustrační schéma paměti (ve skutečnosti jsou políček miliony)
Pointery Teď už vám prozradím, co přesně je pointer. Je to vlastně úplně normální proměnná (identická s int, long nebo long int), a číslo, které jí přiřadíme, je adresa. Stejně jako normalní int se ukládá na stack a když se skončí funkce, ve které je deklarována, umaže se. Přesně jako každá normální proměnná.
Takový pointer tedy obsahuje adresu. Ta adresa nám ukazuje do paměti, na nějaké políčko (100 na 100. políčko, 101 na 101. políčko atp). Ale kompilátor ještě chce vědět, na co pointer ukazuje. Jestli na tom místě, na které ukazuje, leží (začíná) float, nebo jestli tam leží třeba char. To mu také musíme říct.
Jak se deklaruje pointer Pokud bychom tedy chtěli nadeklarovat proměnnou pointer, budeme postupovat takto typ_promenne_na_ktery_pointer_ukazuje *jmeno_promenne_tedy_vlastne_pointeru; Mnoho lidí pointery nepochopí, přitom je to snadné. Pointer (to, že je to pointer a ne obyčejná proměnná, značí právě ta * v jeho deklaraci) je vždy proměnná, která obsahuje číslo. Ta proměnná má v 32-bitových systémech jako Windows vždy velikost 4 byty (aha! 4 byty je 32 bitů!). To, na co ukazuje pointer, se překladači řekne na přes typ_promenne_na_ktery_pointer_ukazuje. Tohle je opravdu důležité, klidně si to pročtěte vícekrát, nesyďte se. Celé se to potom čte jako pointer na typ, konkrétně tedy třeba "pointer na float", "pointer na double" ap. int *pointer_na_int; float *pointer_na_float, *druhy_pointer_na_float;
Kam mohou pointery ukazovat Ne, že by pointery nemohli ukazovat na proměnné na stacku, ale jejich účelem je možnost ukazovat na heap, tedy mimo stack. Problémem stacku totiž bývá, že má často hodně omezenou velikost a proměnné se na něj tedy nevejdou (řekněme, že budeme schopni proměnných udělat tisíce) (když se zamyslíte, že pointer má 4 byty a většina proměnných stejně nebo míň, mělo by vám to přijít podivné nahrazovat malou proměnnou jako char třeba pointerem na char. To vysvětlíme v přespříštím díle)
Shrnutí Tahle lekce byla hlavně teoretická (z praxe jste se akorát dozvěděli, jak se pointer zapisuje). Nesnažte se zatím s pointery pracovat. Ještě zdaleka nevíte jak a taky by se vám mohlo stát (pokud by jste byli dostatečně zvídaví), že se vám povede "shodit" nebo "zaseknout" počítač. Měli by jste si být vědomi toho, co je to stack, že se na něj proměnné přidávají a ubírají se zněj a kdy (ty deklarované ve funkci se přidávají na začátku funkce, odebírají po jejím skončeni). Měli by jste vědět, že heap je nějaký kus paměti. Taky je dobré vědět, jak se zapisuje pointer a co to je.
Programování v C/C++ 9. díl Je tu dokončení minulého dílu o pointrech. Tentokrát už se dozvíme, jak se s pointery pracuje a co všechno se s nimi dá dělat. Na konci nastíníme i k čemu nám budou do budoucna.
Co je to alokace Posledně jsme si řekli, že pointer ukazuje kamsi do paměti. Ovšem my nemůžeme pointer vzít, přiřadit mu libovolnou adresu a na ni jen tak něco zapsat (jak se do poitneru přiřazuje hodnota a jak se poitneru přiřazuje adresa se dozvíte níže. Pokud vám dělá problém rozlišit tyto dva pojmy, přečtěte si znovu minulý díl). Problém by byl v tom, že náš pointer by potom ukazoval na nějaké místo v paměti, kde už mohou být uloženy různé věci. Co kdyby se vám povedlo ukazovat na místo, na které už ukazujete jinde (například když by jedna vaše funkce pracovala s nějakým místem v paměti a poté, o několik týdnu později, by jste dopsali jinou funkci, která by pracovala s tím samým místem paměti, jelikož vy už jste zapomněli, že s tímto místem pracujete)? Funguje to tak, že když chcete něco do paměti uložit, řeknete to systému a ten vám vyhradí určitý úsek z heapu. Současně s tím vám samozřejmě řekne, jakou adresu tento kus heapu má. Tedy chcete-li něco uložit do paměti: · řeknete to systému · systém vám řekne, na jaké adrese začíná pro vás vyhrazená paměť. Tomuto všemu se říká alokace. Vy tedy alokujete například jeden float a operační systém vám řekne, na jakou adresu ho můžete uložit. Stejně je to samozřejmě i s ostatními typy.
Jak se alokuje Když už víme co je to alokace, řekneme si ještě, jak se provádí. Protože víme, že nám systém vrátí adresu do paměti, musíme ji mít kam uložit. Když bychom chtěli uložit adresu například na float, napsali bychom float *poitner_na_float; Což tedy mluví samo za sebe. pointer_na_float je pointer, ve kterém je uložena adresa, která ukazuje na float. Teď když se zeptáme systému, kde bychom mohli svůj float uložit, bude to schematicky (new je anglicky nový, jako "nový kus paměti") nejky_pointer = new typ_na_ktery_bude_pointer_ukazovat; nebo s naším floatem poitner_na_float = new float; Systém teď ví, že má vyhradit paměť pro float, vyhradí ji (na heapu!! nikdy ne na stacku) a vrátí vám adresu na začátek tohoto kusu paměti, kterou vy si uložíte do pointer_na_float.
Dealokace Když už nepotřebujete daný kus paměti používat - taková situace nastává nejčastěji na konci programu - musíte zase systému říct, že už paměť nepotřebujete. Tohle by jste měli dělat vždy! Když to budete opomíjet, může se vám stát, že systému bude docházet pamět (protože vy jste systému neřekli, že už paměť nepotřebujete a on vám jí pořád drží rezervovanou). Je to jako kdyby jste nevraceli knihy do půjčovny. Za chvíli nebude co půjčovat. To je také důvod, proč jsem minule vysvětlil, co je to stack a co je heap. Protože pointer, jako každá normální proměnná, je na stacku, zatímco ty alokovaná data jsou v heapu. Jenomže to, že přestane existovat pointer ještě neznamená, že se odalokují data na heapu. Tohle v budoucnu využijeme ve svůj prospěch, tentokrát to ale uvádím, aby jste věděli, proč se má dealokovat. (důvodem k tomu, že se se zánikem pointeru automaticky nedealokují i data je ten, že na jednu adresu může ukazovat i více pointerů (což není špatně, pokud to tak chcete)) Syntaxe je následující (delete je anglicky smazat) delete nejaky_pointer; V našem případě tedy delete pointer_na_float; Měli by jste dealokovat jenom platné pointery (viz níže).
Práce s pointery Jak je nám známo, pointer je vlastně normální proměnná s hvězdičkou navíc, která uchovává adresu do paměti. Jak ale můžeme pracovat s pamětí, na kterou pointer ukazuje? Jak můžeme do pointeru přiřazovat adresy? Začneme naší druhou otázkou, tedy jak můžeme do pointeru přiřazovat adresy? Celkem jednoduše! Jako kdybychom chtěli do normální proměnné přiřazovat hodnotu. Tedy pointer = adresa; Než budu pokračovat, rád bych se zmínil o tom, že by vám kompilátor neměl dovolit přiřadit do pointeru číslo, které by vyjadřovalo adresu (no neměl, on vám to nedovolí), tedy například pointer_na_float = 456789; //tohle vám kompilátor nedovolí
Kompilátor by vám měl dovolit do pointeru přiřadit jen odpovídající typ pointeru, tedy pointer na float do pointeru na float, do pointeru na char pointer na char ap. float *pointer_1, *pointer_2; char *pointer_na_char; pointer_1 = pointer_2; //tohle jde pointer_na_char = pointer_1; //tohle je spatne! Tak to by byla práce s adresami. Ale co když chcete pracovat s tou proměnnou, na kterou pointer ukazuje? Dělá se to tak, že se v kódu před pointer napíše hvězdička: char *pointer_na_char; char normalni_promenna_typu_char; *pointer_na_char = 25; //tady je videt ta pridana hvezdicka normalni_promenna_typu_char = *pointer_na_char; //tady taky V prvním případě se do kusu paměti (tedy do proměnné typu char uložené na heapu), na který pointer_na_char ukazuje, zkopíruje hodnota 25. V druhém případě se hodnota proměnné, na kterou pointer_na_char ukazuje, zkopíruje do naší normální proměnné. Takový zápis s hvězdičkou pak kompilátor chápe stejně, jako kdyby jste pracovali s normální proměnnou, a čte se jako "to, na co ukazuje pointer na char", tedy například "to, na co ukazuje pointer na char, se rovná 25".
NULL a co je tam, když tam nic není?? Jediná číselná hodnota, kterou vám kompilátor dovolí do pointeru přiřadit, je 0. A to proto, že 0 je dohodou adresa, která je neplatná. Když tedy chcete říct, že pointer ještě na nic neukazuje, přiřadíte mu hodnotu 0. Místo 0 se ale píše NULL, kvůli přehlednosti, což je někde zapsáno jako #define NULL 0 takže je to vlastně to samé jako psát 0. Taky se vám při alokaci může stát, že new vrátí NULL. To znamená, že není dost paměti na heapu pro vaši proměnnou. Je dobrým zvykem kontrolovat, jestli se alokace opravdu povedla. Proč my ale vlastně přiřazujeme pointeru 0? Protože my nevíme, jaká adresa je v pointeru uložena. To je v postatě věc náhody, záleží to na tom co tam bylo předtím (když paměť byla používána jinou aplikací), jestli je zrovna úplněk a podobně. Tedy nic, co bychom dovedli předpovědět. Bude v tom něco naprosto náhodného. Proto je dobré nulovat (nebo NULLovat?;-)) pointery.
Když pointer ukazuje na stack Všechna alokovaná data jsou sice na heapu, ale to neznamená, že pointer nemůže ukazovat na stack! To on totiž může a celkem často se toho využívá. Kdybychom třeba chtěli (z důvodů, které osvětlím v nějakém pozdějším díle) předat jako parametr funkce pointer, ale programátor, který by naši funkci používal, by měl hodnotu uloženou jenom v normální proměnné, tedy na stacku. Když vy chcete pointer a on má jenom normální proměnnou, udělá se to tak, že vám dá adresu na svoji normální proměnnou, tedy na stack. Teď už by jste měli i vědět, proč by v žádném případě neměl zkopírovat hodnotu své proměnné do vašeho pointeru (krom toho, že to nejde nijak zapsat, když předáváme parametry funkci)… zamyslete se… protože vy od něj chcete pointer, tedy místo v paměti, ze kterého si budete "brát" svá data, a on se na nějakou úplně náhodnou adresu (viz výše - ten parametr samozřejmě obsahuje nějakou náhodnou adresu, tedy ukazuje na nějaké špatné místo) snaží něco nahrát. Vy chcete znát místo v paměti, ne zkopírovat do pointeru nějaká data. Z toho plyne, že vy ani ten pointer nealokujete! To jen tak mimochodem. Pokud ale chcete předat adresu na nějakou proměnnou (ve stacku): int *pointer_na_int; int normalni_int; pointer_na_int = &normalni_int; Napíšete před vaší normální proměnnou & a kompilátor ji v tomto zápisu bude vnímat jako pointer. Čte se to vše jako "adresa/pointer na normalni int", respektive "pointer na int se rovná adrese normálního int").
Shrnutí Naučili jsme se, co je to alokace, kde se nachází alokovaná data (heap), proč bychom měli dealokovat, že můžeme ukazovat i na stack, nejenom na heap. Dále také jak se dá pracovat s proměnou, na kterou máme pointer, i jak získat pointer na normální proměnnou.
Slušné chování Je pár zásad, které by jste vždy měli dodržovat. ·
Kontrolovat, jestli jste naalokovali v pořádku (když nebudete a uživatel si pustí k vašemu programu ještě film, bude něco stahovat atp. atp., vaše aplikace spadne).
· · ·
·
Odalokovávat jen platné pointery Vždy zapisovat jen do alokovaných pointerů Pokud používáte adresu na stack, být si jistý(á), že v době, kdy budete s obsahem proměnné, na kterou pointer ukazovat, pracovat, bude proměnná ještě stále na stacku. Tedy když přiřadíte pointeru adresu na nějakou proměnnou z dané funkce a budete s ní chtít pracovat poté, co funkce skončí, program spadne. Proměnná už totiž nebude na stacku, neboť funkce už skončila a všechny její proměnné se tedy vymazali ze stacku (viz minulý díl) být opatrní; když spadne program, je to na 90% kvůli práci s pointery.
Cvičení Napište program, kde budou tři pointery na int. Do prvního se uloží číslo, do druhého číslo a do třetího výsledek násobení. //nase tri pointery int *cislo1 = NULL, *cislo2 = NULL, *cislo3 = NULL; //pokud jsou platne, dealokuje poitnery void Uklid() { if(cislo1 != NULL) delete cislo1; if(cislo2 != NULL) delete cislo2; if(cislo3 != NULL) delete cislo3; } int main() { //naalokujeme cislo1 a zkusime, jestli se nam alokace opravdu povedla cislo1 = new int; //nepovedla, ukoncime program if(cislo1 == NULL) { //uklidime a ukoncime aplikaci Uklid(); return -1; } //naalokujeme cislo2 a zkusime, jestli se nam alokace opravdu povedla cislo2 = new int; if(cislo2 == NULL) { Uklid(); return -1; } //naalokujeme cislo3 a zkusime, jestli se nam alokace opravdu povedla cislo3 = new int; if(cislo3 == NULL) { Uklid(); return -1; } //ted uz mame vsechny tri pointery naalokovane, jdeme s nimi pracovat *cislo1 = 2; //priradime hodnoty *cislo2 = 3; //vysledek do cislo3 *cislo3 = *cislo2 * *cislo1;
printf("%d * %d = %d", *cislo1, *cislo2, *cislo3); Uklid(); } Jan Beneš