PROGRAMOVÁNÍ V C++ STUDIJNÍ OPORA PRO KOMBINOVANÉ STUDIUM
PROGRAMOVÁNÍ V C++ Ing. Jiří BLAHUTA, Ph.D. Moravská vysoká škola Olomouc, o.p.s., 2017
Moravská vysoká škola Olomouc, o. p. s. Autor: Ing. Jiří BLAHUTA, Ph.D. Olomouc 2018
Obsah Úvod 7 Historie C++, základy syntaxe kódu 9 1.1 Stručná historie jazyka C++ 10 1.2 Základní syntaxe jazyka C++ 10 1.2.1 Proces kompilace (překladu) zdrojového kódu v C++ 11 1.2.2 První kód v C++ 13 Základní datové typy 18 2.1 Datové typy v C++ 19 2.2 Proměnné a konstanty 19 2.2.1 Číselné datové typy 20 2.2.2 Nečíselné datové typy 21 2.3 Deklarování proměnných a konstant 22 2.4 Globální a lokální hodnot proměnných 23 2.5 Přetypování 23 2.6 Praktický příklad s komentářem 24 Operátory aritmetické a booleovské 26 3.1 Operátory a jejich typy v C++ 27 3.2 Aritmetické operátory, priorita 27 3.3 Relační operátory 29 3.4 Booleovské (logické) operátory 30
5 HISTORIE C++, ZÁKLADY SYNTAXE KÓDU 3.5 Bitové operátory 32 3.6 Přetěžování operátorů v C++ 32 3.7 Praktický příklad s komentářem 33 Ukazatele, pole, řetězcový a výčtový typ 35 4.1 Ukazatele (pointer) a jejich aritmetika 36 4.2 Pole (array) 39 4.2.1 Programování polí v C++ 39 4.2.2 Vícerozměrná pole v C++ 41 4.3 Řetězcový typ string 41 4.4 Výčtový typ enum 42 4.5 Praktický příklad s komentářem 44 Funkce a procedury 46 5.1 Jádro procedurálního programování 47 5.2 Funkce 47 5.3 Procedury 50 Uživatelské vstupy 52 Typy struct, union, typedef a alokace paměti 57 7.1 Struktury struct 58 7.2 Unie, union 59 7.3 Uživatelský datový typ 59 7.4 Dynamická alokace paměti 60 Podmínky, přepínač switch-case 62 8.1 Podmínka if 63 8.1.1 Logické operátory v podmínkách 65 8.1.2 Varianty podmínky if 66 8.2 Přepínač switch-case 68 8.3 Zachycení a ošetření výjimek 69 Cykly for, while, do-while 71 9.1 Cyklus for 72
9.2 Cyklus while a cyklus do-while 73 9.3 Praktický příklad s komentářem 74 Práce se soubory v C++ 77 10.1 Otevření a zavření souboru 78 10.2 Práce s daty v souboru čtení/zápis 79 10.3 Kompletní příklad s komentářem 81 Rekurze princip, využití 83 11.1 Tři příklady využití rekurze 85 Makra a funkce s proměnným počtem parametrů 91 12.1 Direktivy #define a #undef 93 12.2 Podmíněná makra 95 12.3 Funkce s proměnným počtem parametrů 95 Závěr 98 Doporučená literatura 100 Webové zdroje 100
Úvod Cílem předmětu Programování I je seznámit studenty se základy programování v jazyce C++. Tento jazyk patří mezi jazyky strukturovaného programování, podobně jako C, z něhož je jazyk C++ ve velké míře odvozen. Programovací jazyk C++ patří mezi multiparadigmatické jazyky, což znamená, že lze v něm programovat strukturovaně i dle paradigmatu objektově-orientovaného programování (OOP). Tento předmět je zaměřen na strukturovaný přístup. Po dlouhou dobu se jako první programovací jazyk vyučoval Pascal, ten však již nedostačuje současným požadavkům, ale C++ je stále oblíbeným a používaným jazykem. Programování je proces zápisu navrženého algoritmu v konkrétním programovacím jazyce. Programovacích jazyků je spousta, můžeme je rozdělit dle spousty kritérií. Pro vývoj desktopových aplikací je jedním z používaných právě C++ vedle jazyka C#, Java nebo například Python. Jazyk C++ je vhodný i pro začínající programátory, neboť má v sobě logiku jak procedurálního přístupu blízkého např. k Pascalu, tak i moderní OOP. Zápisu v programovacím jazyce říkáme zdrojový kód. Tato studijní opora je rozdělena do 12 kapitol, které na sebe tematicky navazují. Od historie jazyka C++, přes základní algoritmické konstrukce až po využití rekurze a maker. V některých kapitolách jsou uvedeny na konci souhrnné komentované příklady, na nichž je ukázána probíraná problematika, např. použití cyklů nebo ukazatelů. U studentů tohoto předmětu se předpokládají základní znalosti algoritmizace v obecném smyslu, například co je cyklus, soubor nebo funkce. Cílem předmětu není výuka obecných technik algoritmizace, nýbrž konkrétní programování v jazyce C++. Není vyžadována předchozí znalost žádného z programovacích jazyků, nicméně alespoň základní znalosti zejména jazyka C jsou určitě nesporným kladem pro rychlejší pochopení. Jazyky C a C++ jsou si velmi blízké, naopak sem nepatří C#, které je položen na základech OOP.
K praktickému programování lze použít nejen běžný programátorský editor jako je PSPad či Notepad++, ale rovněž plnohodnotná vývojová prostředí vybavená C++ kompilerem. Vhodným prostředím je například NetBeans 1, Eclipse IDE 2 či Microsoft Visual C++ 3. Bez nutnosti instalace můžeme zejména pro jednoduché programy využít online kompiler Cpp.sh 4. Umění programování nespočívá jen v naučení se syntaxe programovacího jazyka, ale především chápat souvislosti a jakým způsobem lze kód optimalizovat pro určité situace. Programování tak spojuje algoritmické analytické myšlení společně se zvládnutím programovacího jazyka. Programování není učení se nazpaměť. Budete-li umět kód nejen syntakticky správně napsat, ale budete-li ho chápat, pak není obvykle velkou překážkou tentýž kód naprogramovat v jiném programovacím jazyce, pokud budete rovněž chápat jeho syntaxi. Předpokládáme, že výuka bude probíhat v počítačové učebně s potřebným vybavením a studenti si tak ihned mohou programy zkoušet, což je stěžejní základ pro výuku programování. Všechny uvedené příklady byly pečlivě odzkoušeny a předpokládá se jejich podrobnější vysvětlení při výuce, zejména u složitějších příkladů. Příklady jsou zvoleny úměrně podle náročnosti a cílové skupině především začínajících programátorů. K mnohým tématům se tato studijní opora vůbec nedostane, vzhledem k vymezenému rozsahu. Je to například programování aplikací s grafickým rozhraním (GUI). Cílem této opory není dokonalé zvládnutí jazyka C++, ale být pomocníkem začínajících programátorů, aby si ujasnili základní syntaxi a základní prvky jazyka C++. A našli v jazyce C++ oporu jako vhodný start pro programování, jelikož má tento jazyk široké možnosti využití. 1 https://netbeans.org/features/cpp/ 2 https://www.eclipse.org/downloads/packages/eclipse-ide-cc-developers/keplersr2 3 https://www.visualstudio.com/cs/vs/cplusplus/ 4 http://cpp.sh/
Kapitola 1 Historie C++, základy syntaxe kódu Po prostudování kapitoly budete umět: stručně srovnat programovací jazyky C a C++; jednoduše vysvětlit princip a význam kompilace kódu v C++; napsat první kód v C++ a pochopit základní prvky jeho syntaxe. Klíčová slova: Jazyk C a C++, historie C++, syntaxe C++, zdrojový kód, kompilace, kompiler, překlad, direktivy, preprocesor, blok.
PROGRAMOVÁNÍ V C++ 10 1.1 Stručná historie jazyka C++ Programovací jazyk C++ sdílí v mnohém společné prvky s jazykem C. Je velmi oblíbený pro svou multiparadigmatickou univerzálnost lze v něm programovat strukturovaně, pomocí OOP a využít generické programování. C++ tak není čistě procedurální, ani čistě objektově-orientovaný jazyk. Vznikl v roce 1983 v laboratořích AT&T, vyvinul jej Bjarne Stroustrup 5 jako rozšíření pro jazyk C, který v tehdejších dobách byl na vrcholu společně s jazykem Pascal. Z těchto důvodů má C++ mnohé rysy společné s C, ovšem přidává mnoho nových vlastností a funkcionalit. Na začátek řekněme tohle: Každý program, který lze zkompilovat pomocí C kompileru lze současně zkompilovat i pomocí C++ kompileru. To je sice pravda, nicméně trochu přesněji: Jazyk C++ zpětně podporuje jazyk C. To znamená, že základy jsou společné s původním jazykem C, a C++ přidává nové prvky syntaxe a některé věci se v C++ zapisují odlišně nežli v C. 8 základních vlastností jazyka C++ v kostce: C++ je ISO standardizovaný od roku 1998 C++ je kompilovaný programovací jazyk multiparadigmatický programovat lze strukturovaně i dle OOP vysoká míra kompatibility s C více než 3000 dostupných knihoven pro C++, standardní knihovna šablon STL automatické alokace a dealokace paměti přetěžování funkcí a operátorů datový typ bool s logickými hodnotami true či false C++ se uplatňuje jako progresivní jazyk i pro řešení náročných úloh v mnoha vědních oborech, viz (Bronson, 2012). 1.2 Základní syntaxe jazyka C++ Programování je zápis algoritmu v programovacím jazyce, který poté překladač přeloží do strojového kódu pro počítač. C++ patří mezi vyšší programovací jazyky, což znamená, že programujeme 5 http://www.stroustrup.com/
11 HISTORIE C++, ZÁKLADY SYNTAXE KÓDU v kódu, který srozumitelný pro nás. Narozdíl od nižších programovacích jazyků, u nichž je nutno programovat na úrovni procesorových instrukcí a symbolických adres. C++ patří ke kompilovaným jazykům, což znamená, že zdrojový kód je přeložen překladačem do spustitelného programu, u Windows je to EXE aplikace. Samotné soubory s kódem v C++ mají příponu.cpp. Programování ve vyšších programovacích jazycích tedy znamená, že zapisujeme algoritmu srozumitelnými zápisy, které mají danou syntaxi. Každý programovací jazyk má sadu klíčových slov, pomocí nichž se zapisují příkazy s daným významem. Syntaxe je poměrně striktně dána, což má výhodu v tom, že pochopímeli, jak daný příkaz funguje, můžeme jej úplně přesně stejně aplikovat jinde. Například zápis const int petka = 5; říká, že do programu zavádíme (deklarujeme) konstantu s názvem petka, má datový typ integer a její hodnota je číslo 5. O tom všem bude postupně řeč. C++ patří mezi poměrně univerzální programovací jazyky a má široké využití v řadě aplikací, nejen díky svému multiparadigmatickému přístupu. Některé jazyky jsou sice velmi výkonné pro určitý typ programů, ale jsou omezené svou univerzálností, patří sem například Lisp, Haskell či matematický Matlab. Naopak C++ lze použít pro různé typy aplikací. Jedna důležitá věc na začátek C++ je casesensitive, tedy rozlišují se malá a velká písmena v kódu a tudíž proměnná s názvem Vypocet není to samé jako vypocet nebo VYPOCET. Totéž platí o definovaných funkcích, procedurách, apod. 1.2.1 Proces kompilace (překladu) zdrojového kódu v C++ C++ patří mezi kompilované jazyky (narozdíl například od C#, který je interpretovaný), což znamená, že každý zdrojový kód v C++ se musí přeložit tzv. kompilátorem (kompilerem) do binárního kódu, kterému rozumí procesor. Proces kompilace má 3 fáze - preprocessing (předzpracování), překlad do objektových souborů, volání funkcí a linkování. Nebudeme se zde zaobírat jednotlivými fázemi, velmi podrobně lze celý proces pro C a C++ prostudovat například na webu ITnetwork.cz 6. V unixových systémech včetně Linuxu je základním překladačem (kompilerem) gcc 7. Ve Windows nejčastěji kompiluje přímo vývojové prostředí, s nímž pracujeme, např. již zmíněné Microsoft Visual Studio. Použití vývojového prostředí je nejpohodlnější cesta pro programování, neboť nabízí programování, ladění (debugger) i kompilaci a spuštění programu. Podstatné je vědět to, že pokud uděláme ve zdrojovém kódu jakoukoli změnu, je třeba kód znovu přeložit pro správné spuštění programu. Přehledně proces popisuje následující obrázek 8. 6 https://www.itnetwork.cz/cplusplus/kurz/pokrocile/cplusplus-tutorial-kompilace-v-jazyce-c-a-cplusplus 7 https://gcc.gnu.org/ 8 https://www.ntu.edu.sg/home/ehchua/programming/cpp/cp0_introduction.html
PROGRAMOVÁNÍ V C++ 12 Obrázek 1.1: Proces kompilace C++ V krocích (step) 2 až 4 probíhá proces sestavení (build) a kroky 5 a 6 představují samotné spuštění programu. Stručně se zmíníme o preprocesoru a tzv. direktivách. Než dojde k samotnému překladu zdrojového kódu, jak jsme jej popsali, kód je upraven tzv. preprocesorem. Pokyny pro preprocesor řídí tzv. direktivy. Ta je ta úplně první část ve zdrojovém kódu, ještě před samotným začátkem funkce main. Všechny direktivy začínají znakem #. Takže například direktiva #include <cstdio> vloží obsah souboru (knihovny) cstdio, resp. v jazyce C studio.h (tzv. hlavičkové soubory). Obvykle se setkáme s direktivou #include pro použití různých knihoven, existuje však mnoho dalších direktiv 9. Z většinou z nich se v tomto textu vůbec nesetkáte. Příkladem direktiv #include je například: #include <iostream> #include <cmath> #include <cstdio> #include <iomainp> 9 http://www.sallyx.org/sally/c/c11.php
13 HISTORIE C++, ZÁKLADY SYNTAXE KÓDU Například makra se definují za direktivou #define a zrušit je lze direktivou #undef. O makrech je pojednáno v poslední 12. kapitole. Seznam standardních knihoven pro C++ najdete např. na webu Cppreference.com 10 včetně jejich popisu. 1.2.2 První kód v C++ Nyní je na čase na praktickou ukázku prvního zdrojového kódu v jazyce C++. Ve většině učebnic se začíná příkladem kódu, který vypíše "Hello world" neboli česky "Ahoj světe". Trochu nejprve odbočíme od tohoto zvyku a necháme si vypočíst součet čísel 5 a 8. K tomu budeme potřebovat dvě proměnné - nazveme je scitanec1 a scitanec2, obě budou mít datový typ integer. Výsledek zapíšeme do proměnné součet. Kód bude vypadat takto: int scitanec1 = 5; int scitanec2 = 8; int soucet = scitanec1+scitanec2; Co vše to znamená, si vysvětlíme v další kapitole. Není to však úplný program, je to pouze definice proměnných a operace přiřazení. Musíme si vysvětlit rozdíl mezi tzv. definicí proměnné (funkce, konstanty) a deklarací. Deklarace je pouze zapsání datového typu a názvu. Definice přímo definuje obsah proměnné, resp. tělo funkce. Příklad v následujícím kódu: byte promennaa; // deklarace promenne s nazvem (identifikátorem) promennaa promennaa=20; // definice - promennaa bude mit hodnotu 20 Často se na začátku programu proměnné, konstanty či funkce pouze deklarují a teprve později se definuje jejich obsah. Viz kapitola o funkcích. Přesto si uvedeme kompletní kód programu "Hello World", v němž necháme na obrazovku vypsat uvítací hlášku. Pokud píšeme program ve vývojovém prostředí (viz úvodní kapitola), ihned jej můžeme nechat zkompilovat a spustit. // muj prvni program v C++, ktery nas uvita #include <iostream> 10 http://en.cppreference.com/w/cpp/header
PROGRAMOVÁNÍ V C++ 14 using namespace std; // pouziti jmenného prostoru std int main() //bezparametricka funkce main { cout << "Ahoj, tady C++!"; return 0; Na první pohled to vypadá složitě, ale projděme si jednotlivé řádky. // muj prvni program v C++, ktery nas uvita je to pouze komentář, která nemá vliv na chod programu #include <iostream> - zahrnutí standardní knihovny iostream pro vstupně-výstupní operace, např. právě výpis na obrazovku; nahrazuje hlavičkové soubory z jazyka C using namespace std; - užití jmenného prostoru std (standardní množina funkcí std) int main() standardní funkce main bez parametrů, kterou musí obsahovat každý program v C++ { - složené závorky se užívají pro ohraničení tzv. bloku, tzn. celku obsahující příkazy cout << "Ahoj, tady C++!"; - samotný výpis hlášky "Ahoj, tady C++!" na obrazovku (vypíše se bez uvozovek, ty označují textový řetězec), operátor << má kromě bitového posuvu význam jako směrovač výstupu (právě zde) return 0; - ukončení funkce main a konec celého programu v C++ (číslo 0 značí bezchybné ukončení programu) - ukončení bloku Poznámka k funkci cout, kterou budeme často používat pro standardní výstup. Za operátorem << může být jakákoli číselná, textová nebo znaková konstanta či proměnná. Konec řádku lze zapsat pomocí \n nebo endl (z angl. end of line) cout << Text a konec řádku\n ; cout << Zakončení řádku jinak << endl; Jakmile se dostaneme průběžně dále, význam jednotlivých celků Vám bude jasný. Prozatím jsme napsali první program v C++. Ještě malou odbočku k jazyku C, v němž by zápis kódu téhož programu vypadal de facto obdobně: /* uvitaci program v C */ #include<stdio.h>
15 HISTORIE C++, ZÁKLADY SYNTAXE KÓDU main() { printf("ahoj, tady C!"); Rozšířený Hello World o zadání vstupu: #include <iostream> #include <string> using namespace std; // zde zacina vlastni program funkci main int main() { string jmeno; cout << "Jak se jmenujes? "; // vypsani otazky na jmeno getline (cin, jmeno); // precteni jmena cout << "Ahoj, " << jmeno << "!"; // vypis pozdravu se jmenem Tímto jsme si vysvětlili zcela první kód v programovacím jazyce C++. Nyní si vysvětlíme pojem blok. Zdrojový kód se skládá z bloků. Vše, co je uzavřeno mezi závorky { a, je blok. Obecně jej lze zapsat takto: int funkce(void) { příkazy Rovněž se blok používá u podmínek: podmínka { příkazy
PROGRAMOVÁNÍ V C++ 16 Blok je tedy logickým celkem příkazů jedné funkce, podmínky, apod. Na začátku ještě zmiňme použití komentářů v kódu. Komentáře jsou pomocné části kódu, které nic nevykonávají. Komentář slouží k tomu, abychom vysvětlili v kódu danou funkci, apod. V jazyce C++ používáme dva typy komentářů: jednořádkový komentář zapisujeme pomocí // víceřádkový komentář ohraničuje jej syntaxe /* a */ (vše mezi tím je komentář) Příklad komentáře v kódu byte promennax; // deklarace promennax s datovym typem byte /* promennax bude vždy 25 nebo 50 nyní nastavime 25 */ promennax=25; Komentáře budeme v příkladech velmi často používat pro vysvětlení. Komentáře mají rovněž velký význam zejména u rozsáhlejších programů, kde je možno komentovat význam proměnných, které deklarujeme. Kód bude přehledný a jednoduše se v něm vyznáme i po delším čase. Srovnejte srozumitelnost dvou deklarací: int d; char s; bool k; Po čase my sami a nikdo jiný nebude tušit, co proměnné znamenají a obtížně je budeme v kódu dohledávat. Mnohem lepší bude následující deklarace s komentáři: int davka_leku; // davka leku denne 0 az 5 char stupen_progrese; // stupen progrese nemoci A, B nebo C bool kontrola_6; // kontrola za poslednich 6 mesicu ano-ne Takto budeme vědět, co proměnné reprezentují i jaké mají hodnoty. Chceme-li omezit určitý rozsah, můžeme definovat tzv. výčtové typy enum, viz kap. 7.
17 HISTORIE C++, ZÁKLADY SYNTAXE KÓDU Programovací jazyk C++ patří mezi univerzální, vyšší programovací jazyky. Jeho předností je multiparadigmatický přístup pro procedurální i OOP programování. Historie je spjata s jazykem C, který se stejně jako C++ stal ISO standardem. C++ je kompilovaný jazyk. Proces kompilace je nezbytný ke spuštění programu a vytvoření spustitelného souboru. Tím, že jazyk C++ má mnoho společného s jazykem C, mnohé postupy jsou podobné, i když se syntaxe kódu liší. V kapitole je uveden a vysvětlen kód základního programu Hello World a ukázka deklarace proměnné. 1. Stručně uveďte základní poznatky mezi C++ a C. 2. K čemu je kompilace neboli překlad zdrojového kódu v C++? 3. Jednoduše vysvětlete kód programu Hello World. Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5.
Kapitola 2 Základní datové typy Po prostudování kapitoly budete umět: používat správně datové typy pro konstanty a proměnné; rozumět deklaraci proměnných a konstant; chápat význam a použití přetypování. Klíčová slova: Proměnná, konstanta, datové typy, C++ deklarace, celočíselné typy, přetypování.
19 ZÁKLADNÍ DATOVÉ TYPY 2.1 Datové typy v C++ Porozumění datovým typům patří mezi základní požadavky a nutné znalosti každého programátora. V jazyce C++ je možno využívat řadu datových typů a jejich vzájemné přetypování, tzn. převedení proměnné z jednoho datového typu do jiného. V této kapitole si vysvětlíme, jaké druhy číselných a nečíselných datových typů existují a jak je použít v kódu programu. Obecně se rozdělují datové typy na číselné a nečíselné dle jejich obsahu. Proměnná nebo konstanta může obsahovat číslo, znak, text nebo logickou hodnotu. Každý číselný datový typ má svůj rozsah, tedy jakých hodnot (čísel) může nabývat. Pokud např. víme, že určitá proměnná má vždy rozsah od -40 do 80 (například teplota), postačí nám datový typ char a nemusíme použít typ, jehož rozsah je v miliardách. Velmi důležitá poznámka na začátek pro číselné datové typy je nutno používat pro desetinná čísla desetinnou tečku a nikoli čárku, jak je zvykem u nás. 2.2 Proměnné a konstanty Proměnná je údaj, který se v průběhu programu může měnit, tedy hodnota proměnné. Může to být číslo nebo znaky. Naopak konstanty jsou údaje, které jednou v programu definujeme, a v průběhu se nemohou měnit. Proměnné i konstanty mohou mít různý datový typ. Každou proměnnou i konstantu musíme v programu deklarovat, tedy zavést. Musíme uvést její datový typ, název (tzv. identifikátor). Následně proměnnou či konstantu definujeme (může být definice rovnou), viz úvodní kapitola 1.1. Co se týče názvu, lze jej zvolit libovolně, avšak nesmí být žádným klíčovým slovem z jazyka C++, viz následující tabulka (Virius, 2012):
PROGRAMOVÁNÍ V C++ 20 Tabulka 2.1 Klíčová slova v jazyce C++ alignas alignof asm auto bool break case catch char char16_t char32_t class const constexpr const_cast continue decltype default delete double do dynamic_cast else enum explicit export extern false float for friend goto inline int long mutable namespace new noexcept nullptr operator private protected public register reinterpret_cast return short signed sizeof static static_assert static_cast struct switch template this thread_local throw true try typedef typeid union unsigned using virtual void volatile wchar_t while Jak již bylo uvedeno, vše je case-sensitive, tedy rozlišují se malá a velká písmena. Nyní už přejdeme konkrétně k jednotlivým datovým typům. 2.2.1 Číselné datové typy Vyjadřují čísla v různých formátech. Mezi základní typy patří signed char, short, integer, označovaný klíčovým slovem int, float a double. Typy int a double mají rovněž dlouhou variantu long int a long double. Pozor, neplést s nečíselným typem char, jenž reprezentuje 1 znak, signed char je náhradou za datový typ byte s rozsahem -127 až 128 používaný v jazyce C. Číselné typy rozdělujeme na celočíselné, tzv. integer a s neceločíselné. Celočíselné typy jsou char, short, int a všechny jejich varianty long. Pro desetinná čísla jsou určeny typy float a double, resp. long double. V C++ mají jednotlivé datové typy ještě variantu signed a unsigned, např. unsigned int. Je to z anglického sign čili znaménko a platí, že pro signed jsou povoleny záporné hodnoty, pro unsigned nikoli. Tím se mění i rozsah, např. short má rozsah -32768 až 32767, kdežto unsigned short má
21 ZÁKLADNÍ DATOVÉ TYPY v kladných hodnotách dvojnásobný rozsah, tedy 0 až 65535. Podívejme se na rozsahy jednotlivých číselných datových typů v C++, které lze použít (Virius, 2012). Tabulka 2.2 Rozsahy číselných datových typů datový typ rozsah počet bitů signed char -127 až 128 8 unsigned char 0 až 255 8 short -32768 až 32767 16 unsigned short 0 až 65535 16 int -2 147 483 648 až 2 147 483 647 32 long int 0 až 4 294 967 265 32 float -3,4 10-38 až 3,4 10 38 7* double -1,7 10-308 až 1,7 10 308 15* long double -1,7 10-308 až 1,7 10 308 15* * u typu float, double a long double je uveden počet číslic U dalších typů, jako long int, unsigned long int, apod. je rozsah dán konkrétním překladačem. Reálná čísla typu float, double a long double jsou v paměti počítače uložena jako dvojice mantisaexponent. 2.2.2 Nečíselné datové typy Proměnné nebo konstanty nečíselných datových typů vyjadřují znak, řetězec znaků nebo logickou booleovskou hodnotu. K nečíselným datovým typům patří char a bool. Bool je datový typ, jehož proměnná může nabývat logických binárních hodnot true či false. Velmi užitečný datový typ v podmínkách (viz kap. 8). Příkladem deklarace je bool lichy = true; Dalším používaným datovým typem je string pro textové řetězce. Je však nutno zavést knihovnu string direktivou #include. Příklad definice řetězce:
PROGRAMOVÁNÍ V C++ 22 #include <string> int main() string retezec = Dlouhá věta definovaná jako proměnná typu string ; Další informace k definicím a deklaracím podrobněji viz další podkapitola. 2.3 Deklarování proměnných a konstant Proměnnou deklarujeme například takto: long int vzdalenost; Deklarací tak říkáme, jaký má proměnná datový typ. Poté použijeme operátor přirazení a proměnné přiřadíme hodnotu, např. 100. To je definice proměnné. vzdalenost=100; Od této chvíle tak v programu má proměnná hodnotu 100, dokud ji nějak nezměníme. Konstantu deklarujeme obdobně, jen přidáme klíčové slovo const, takže například takto: const signed char nasobek = 10; // pro nasobeni 10 Kdykoli v programu lze použít konstantu násobek pro vynásobení 10 a hodnota konstanty je stále stejná, čili konstantní. S dalšími datovými typy jako je enum, struct či union se setkáme v dalších kapitolách včetně definice vlastních datových typů pomocí typedef.
23 ZÁKLADNÍ DATOVÉ TYPY 2.4 Globální a lokální hodnot proměnných Představme si následující fragment kódu, v němž definujeme dvakrát hodnotu téže proměnné pocet: double pocet=5.58; // globalni hodnota promenne pocet void funkce() { double pocet=14.25; // lokalni hodnota promenne pocet cout << pocet << endl << ::pocet <<endl; /*nejprve se vypise hodnota lokalni a na druhy radek globalni stejne promenne*/ Jinými slovy, pokud definujeme proměnnou globálně a lokálně potom v těle funkce, lze tyto definice odlišit operátorem :: (tzv. čtyřtečka). S tímto operátorem se v jiném kontextu setkáme též u funkce cout, resp. cin, pokud nedeklarujeme použití jmenného prostoru std. 2.5 Přetypování Při práci s různými datovými typy mnohdy potřebujeme převést proměnnou nebo konstantu na jiný datový typ. Tomu říkáme přetypování. V jazyce C++ lze používat přetypování implicitní a přetypování explicitní. implicitní z datového typu menšího rozsahu na datový typ většího rozsahu, např. z int na double explicitní z datového typu většího rozsahu na datový menšího rozsahu, např. z float do int Je však nutno si uvědomit, že zatímco při implicitním přetypování se neztrácí žádná informace přetypované proměnné či konstanty, u explicitního přetypování je nutno počítat s možnou ztrátou. Např. uvedené přetypování z datového typu float na celočíselný int ztratíme část za desetinnou čárkou.
PROGRAMOVÁNÍ V C++ 24 Jak na přetypování v C++ prakticky? Je to poměrně snadné před daný výraz uvedeme do závorky požadovaný datový typ, do něhož chceme proměnnou převést. To může ve zdrojovém kódu vypadat například takto: int pocet, suma // na zacatku obe promenne deklarovany jako int // zde je kod programu double aritprumer=(double)suma/(double)pocet // vypocet aritmetickeho prumeru s pretypovanim promennych suma a počet To je pouze jeden konkrétní případ, kde lze přetypování použít a je víceméně nutné. 2.6 Praktický příklad s komentářem Počínaje touto kapitolou vždy bude na konci každé kapitoly příklad s vysvětlením jednotlivých částí zdrojového kódu. Příklad zahrnuje probrané prvky dané kapitoly. Pro ukázku práce s proměnnými jsme vytvořili program, který vypíše plochu obdélníka na základě zadané šířky a délky. #include <iostream> int main() { using std::cout; using std::endl; // deklarace promennych sirka, delka unsigned short sirka, delka; sirka = 15; delka = 20; // deklarace promenne plocha s datovym typem int int plocha = (int)sirka * (int)delka; // ukázka pretypovani do int
25 ZÁKLADNÍ DATOVÉ TYPY cout << "Sirka je: " << sirka << "\n"; cout << "Delka je: " << delka << "\n"; cout << "Plocha obdelniku je: " << plocha << endl; return 0; Co by se stalo, kdybychom použili pouze short namísto unsigned short? Mohli bychom do proměnných sirka a delka zadávat záporné hodnoty, viz tabulka rozsahů číselných datových typů. To je však v tomto příkladu zcela nesmyslné, aby byly proměnné záporné. Proměnnou plocha je nutno deklarovat s datovým typem int, nikoli unsigned short. Protože podle tabulky rozsahu bychom mohli zadat sirka a vyska až do 32767 a součin 32767 32767 je jistě větší než rozsah unsigned short, tedy 32767. V příkladu je použito přetypování proměnných sirka a delka. Všimněte si, že k výpočtu plochy je použit součin, tedy operátor součinu. O jednotlivých typech operátorů bude pojednávat další kapitola. Datové typy jsou stěžejní částí deklarace proměnné či konstanty. Proměnná může měnit svou hodnotu v průběhu programu, konstanta nikoli. Datový typ určuje, zda je proměnná či konstanta číslo, text nebo logická hodnota. U číselných datových typů rozlišujeme celočíselné a neceločíselné datové typy. Potřebujeme-li změnit v průběhu datový typ, použijeme tzv. přetypování z jednoho datového typu na jiný. V kapitole je uveden příklad použití deklarací a přetypování. 1. K čemu slouží datové typy a proč je musíme deklarovat s proměnnou? 2. Uveďte základní číselné datové typy. Co znamená signed a unsigned? 3. K čemu slouží přetypování? Na co je nutno dávat pozor při explicitním přetypování? Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5
Kapitola 3 Operátory aritmetické a booleovské Po prostudování kapitoly budete umět: používat aritmetické, relační a logické operátory v C++; správně používat prioritu a chápat její význam; programovat v C++ jednoduché výpočetní úlohy s aritmetickými operacemi. Klíčová slova: Operátory, operátory aritmetické, priorita operátorů, operátory logické, operátory bitové, relační operátory, přetěžování operátorů.
27 OPERÁTORY ARITMETICKÉ A BOOLEOVSKÉ 3.1 Operátory a jejich typy v C++ V předchozí kapitole jsme si ukázali, jak používat v programování různé datové typy a deklarovat proměnné a konstanty. To však samotné je poměrně k ničemu, potřebujeme provádět operace. Proto se seznámíme v této kapitole s dalším stěžejním prvkem programování - operátory. Operátory úzce související s proměnnými a konstantami. Umožňují provádět operace mezi nimi. V jazyce C++ se setkáváme s několika druhy operátorů. Patrně nejznámější pro všechny jsou aritmetické operátory, které jsou analogií matematických aritmetických operací s čísly. Například výraz 5+6 má dva operandy (čísla 5 a 6) a operátor sčítání (znak +). Operátory rozlišujeme aritmetické, logické a relační. V matematických výrazech se dodržuje používaná priorita operátorů. Mezi aritmetické operátory patří +,-,*,/,^. Operátor minus může být binární i unární. Binární minus je znakem pro odečítání, unární minus je u záporného čísla. Logické operátory se používají ve výrokové logice a jsou to not, and, or, xor, implikace a ekvivalence. Relační operátory porovnávají hodnoty či výrazy a patří mezi ně =, <,>, <=. >= a <>. Nyní detailněji k jednotlivým typům a jak je použít přímo v C++. Kompletní seznam využitelných operátorů v C++ lze najít např. na webu Cppreference.com 11. V tomto textu si uvedeme pouze ty základní, s nimiž se běžně setkají i začínající programátoři. 3.2 Aritmetické operátory, priorita Aritmetické operátory jsou ty, které známe z hodin matematiky, tedy +,-,*, a /. Velmi důležitá je priorita operátorů. Jistě víte, že například součin má vyšší prioritu než součet a tudíž výsledkem 10+10 30 není 600, nýbrž 310, jelikož nejprve se spočte součin 10 30 a poté se přičte číslo 10. Jazyk C++ samozřejmě respektuje používanou prioritu operací. Příklad s proměnnými: int cislo1, cislo2, cislo3; int vysledek; cislo1=100; cislo2=500; 11 http://cs.cppreference.com/w/cpp/language/operator_precedence
PROGRAMOVÁNÍ V C++ 28 cislo3=40; vysledek=cislo1-cislo2*cislo3; Správný výsledek je v tom případě -19900. Vidíte, jakou zásadní roli priorita hraje? Kdyby se nebrala v potaz priorita a příklad byl počítán zleva doprava jak jde, výsledek by byl 24000. Pozor na celočíselné dělení dvou čísel typu int. Pro dělení dvou čísel typu int bude výsledek typu float, protože dělení není uzavřenou operací nad celými čísly. Tudíž výsledek dělení musíme deklarovat jako typ float, případně double. Obecně nejvyšší prioritu ve vyhodnocování výrazů mají závorky, stejně jako znáte z matematiky. Pokud výraz závorky neobsahuje, je pořadí priorit operátorů následující od nejvyšší po nejnižší: 1. ^ (umocnění) 2. - (unární minus) 3. * a / (součin, podíl) 4. + a (součet, rozdíl) 5. všechny relační operátory 6.! (logická negace) 7. && (logický operátor konjunkce) 8. (logický operátor disjunkce) O logických a relačních operátorech v dalších podkapitolách. K unárním operátorům patří i unární plus, pokud potřebujeme exaktně zapsat kladnou hodnotu a dále operátor inkrementace ++ a dekrementace --, což je tzv. počítadlo neboli postupné přičítání či odečítání konstantní hodnoty. Význam mají tyto operátory především v cyklech (viz kap. 8). K aritmetickým operátorům patří též modulo (zbytek po celočíselném dělení) a zapisuje se operátorem %. Tedy např.: int a, b, m; a=12; b=5; m=a%b; Proměnná m se rovná v tomto případě m=2 (12/5 = 2 a zbytek 2).
29 OPERÁTORY ARITMETICKÉ A BOOLEOVSKÉ V programování stejně jako v matematice samozřejmě platí, že zcela nejvyšší prioritu mají vždy závorky před všemi operátory. A rovněž platí známé matematické vlastnosti jako distributivnost, asociativita a komutativita binárních operací. 3.3 Relační operátory Relační operátory slouží k porovnávání a též je dobře známe z matematiky, například ze zápisů nerovnic typu x + 6 > 2x - 2, relačním operátorem je zde znak nerovnosti, tedy > (větší než). V jazyce C++, podobně jako v jiných jazycích můžeme použít tyto relační operátory: < (menší), > (větší), <= (menší nebo rovno), >= (větší nebo rovno), == (rovná se) a!= (nerovná se). Všimněte si, že operátor rovnosti je "dvojité rovnítko", protože = je operátor přirazení, např. proměnné přiřazujeme hodnotu. Kdežto operátor rovnosti použijeme například v podmínce, zda proměnná x==8. Tedy zápis x=8 znamená, že proměnné x přiřazujeme hodnotu 8, kdežto x==8 znamená, zda je hodnota proměnné x rovna 8. Obecně se relační operátory používají v definicích podmínek, pomocí nichž lze program větvit dle splnění/nesplnění podmínky. Jednoduchý příklad na použití relačního operátoru: if (a >= b) { cout << "Cislo 'a' je vetsi nez 'b' nebo rovno cislu 'b'.\n"; else { cout << "Cislo 'a' je mensi nez cislo 'a'.\n"; Relační operátor == nesmíte zaměňovat s operátorem přiřazení =. Příklad použití v podmínce: int x1=10; int x2=50; if (x1==x2) // rovnost x1 a x2
PROGRAMOVÁNÍ V C++ 30 printf("cisla jsou stejna, tedy x1 je rovno x2."); else { x1=x2+100; // prirazeni x1 bude x2+100 printf("nyni ma cislo x1 hodnotu (x2+100)"); O podmínkách bude podrobně řeč v kapitole 8. Výše uvedená podmínka vypisuje hlášku dle toho, zda je platí a>=b či nikoli. S funkcí cout jsme se již setkali v úvodní kapitole, vypisuje výstup. 3.4 Booleovské (logické) operátory Logické operátory jsou spojky výrokové logiky 12. Nebudeme zde rozebírat princip výrokové logiky a konstrukce výroků. Pouze poznamenáme, že jednoduchým výrokem je např. Každé přirozené číslo umocněné na druhou je sudé. O výroku se dá exaktně dokázat, zda platí nebo platí, například tento neplatí, neboť 1 2 =1 a 1 není sudé číslo. Pomocí logických operátorů spojíme více jednoduchých výroků do výroků složených. Základní logické operátory jsou tyto: negace (not), nebo (or), zároveň (and), vylučovací or (xor), implikace a ekvivalence. V jazyce C++ mají logické spojky, tedy operátory následující způsob zápisu: Tabulka 3.1 Logické operátory a jejich zápis v C++ operátor negace NOT disjunkce OR zápis v C++!(vyrok) výrok1 výrok2 konjunkce AND výrok1 && výrok2 Opět uveďme vysvětlující příklad a opět v podmínce if. 12 http://www.nabla.cz/obsah/vyrokova-logika-vyrok-negace-konjunkce-disjunkce-implikace-ekvivalence.php
31 OPERÁTORY ARITMETICKÉ A BOOLEOVSKÉ if ((a == 5) (b == 5)) // bud a nebo b je rovno 5 cout << "Jedna z promennych ma hodnotu 5.\n"; else cout << "Podminka nebyla splnena.\n"; Podrobněji si rozebereme práci s logickými operátory právě u podmínek. Z výrokové logiky pak vychází pravdivostní tabulka logických spojek. Nechť 1 je logická pravda a 0 nepravda, pak platí tato pravdivostní tabulka. V závorce je uvedený zápis odpovídajícího operátoru v C++. Tabulka 3.2 Pravdivostní hodnoty logických spojek výroka výrokb and (&&) or ( ) xor 0 0 0 0 0 1 1 0 1 0 1 1 0 0 1 0 0 1 1 1 0 Nechť máme 2 jednoduché výroky výroka a výrokb, u nichž lze jednoznačně určit pravdivost. Pak pravdivost složených výroků je dána takto: výroka and výrokb pravda pouze, pokud jsou pravdivé oba dva zároveň výroka or výrokb pravda právě tehdy, pokud je platný alespoň jeden z nich výroka xor výrokb pravda právě tehdy, pokud je platný právě jeden z nich (nikoli však oba) Ve výrokové logice se definují ještě operátory implikace (označovaný ) a ekvivalence (označovaný ). Pravdivost výroků je dána: výroka výrokb pravda právě tehdy, pokud nenastává, že výrok1 je pravdivý a výrok2 nepravdivý výroka výrokb pravda právě tehdy, jsou-li oba výroky nepravdivé či oba pravdivé K booleovským operátorům se jistě vrátím v kapitole o podmínkách.
PROGRAMOVÁNÍ V C++ 32 3.5 Bitové operátory V jazyce C++ můžeme používat bitové operátory. Jejich pochopení vyžaduje již určité zkušenosti. Pro začátečníky to nebude příliš nic použitelného, přesto mají svůj význam. Pomocí nich lze zjistit či nastavit konkrétní bit celého čísla. Existuje celkem 6 bitových operátorů: Bitový posuv vlevo/vpravo, bitový součet, bitový součin, bitová negace a bitová non-ekvivalence. Pozor, již jsme se setkali s operátorem <<, resp. >> v souvislosti s funkcí výpisu výstupu. Tyto dva operátory však mají ještě význam jako bitový posuv vlevo a vpravo. Zkušenější programátoři se mohou o bitových operátorech dovědět detailně např. na webu LearnCpp.com 13. 3.6 Přetěžování operátorů v C++ Přetěžování operátorů již patří mezi pokročilejší techniky v programátorské praxi, nicméně je zde na místě tento pojem alespoň stručně zmínit. Jednoduše řečeno jde o to, že operátor předefinujeme za jiný. Tedy například operátor součinu * může být předefinován, tedy přetěžen např. jako operátor součtu. Nevýhodou je, že nelze přetěžit prioritu, která tak stále zůstane původní a další nevýhodou je, že přetěžený operátor může často mást. Z těchto důvodů například jazyk Java přetěžování nepodporuje, avšak C++ či C# přetěžování dovoluje používat. Například operátory << a >> jsou přetěženy pro funkce cin a cout. Obecně patří přetěžování do OOP v C++, proto se nebudeme tímto zabývat detailně. Přetěžování se provádí pomocí tzv. funkce operátoru. 13 http://www.learncpp.com/cpp-tutorial/38-bitwise-operators/
33 OPERÁTORY ARITMETICKÉ A BOOLEOVSKÉ 3.7 Praktický příklad s komentářem Na závěr kapitoly následuje opět praktický příklad s doprovodným komentářem. #include <iostream> int Soucet(int y, int x) { std::cout << "Ve funkci Soucet(), nastaveno " << y << " a " << x << "\n"; return (y+x); int main() //hlavní funkce main bez parametrů () { using std::cout; using std::cin; cout << "Ve funkci main()!\n"; int a, b, c; // deklarace promennych a,b,c cout << "Zadejte dve cisla: "; cin >> a; // precteni cisla a cin >> b; // precteni cisla b cout << "\nvolame funkci Soucet()\n"; c=soucet(a,b); cout << "\nzpet na zacatku fce main().\n"; cout << "c je rovno " << c; return 0;
PROGRAMOVÁNÍ V C++ 34 Operátory jsou základním prvkem k provádění nejen výpočetních operací s proměnnými a konstantami. Kapitola 3 je zaměřena na použití operátorů v jazyce C++. Operátory rozdělujeme na aritmetické, relační a booleovské (logické). Stěžejní význam má znalost priorit jednotlivých operátorů. U všech typů jsou uvedeny praktické jednoduché příklady použití přímo v kódu C++. Na závěr kapitoly je opět souhrnný komentovaný příklad, na němž je předveden jednoduchý program s využitím operátorů v praxi. 1. Vysvětlete význam priority operátorů a uveďte příklad, kde hraje zásadní roli. 2. K čemu slouží relační operátory a kde se s nimi především pracuje? 3. Jaký je zásadní rozdíl mezi operátorem = a operátorem == a kde se použijí? Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5.
Kapitola 4 Ukazatele, pole, řetězcový a výčtový typ Po prostudování kapitoly budete umět: používat ukazatele a chápat jejich základní aritmetiku; pracovat s jednorozměrnými poli a jejich indexy; použít výčtový a řetězcový typ v C++. Klíčová slova: Ukazatele, pole, výčtový typ, aritmetika ukazatelů, řetězcový typ.
PROGRAMOVÁNÍ V C++ 36 4.1 Ukazatele (pointer) a jejich aritmetika Ukazatele neboli pointery nabízejí odlišný způsob práce s daty než pomocí běžných proměnných. Ukazatel je též proměnná, která však uchovává adresu ukazující do paměti počítače. Paměť je adresována v bytech. Jelikož ukazatele ukazuje na nějakou proměnnou, pak hodnota ukazatele je adresa této proměnné v paměti. Paměť je tvořena souvislou řadou buněk o velikosti 1 bytu. Takže každá proměnná zabere jiný počet buněk v paměti. Důležité je vědět, že každý ukazatel může mít velikost právě 1 bytu, tedy jedné paměťové buňky. Ukazatel uchovává adresu pouze jedné buňky bez ohledu na to, na jakou proměnné ukazuje, proměnná může mít třeba 20 bytů. Ukazatel bude ukazovat pouze na první buňku v paměti. To vše je zajímavé, ale určitě byste rádi viděli, k čemu je to vlastně užitečné. Nejprve si představme za sebou jdoucí paměťové buňky, z nichž každá má jedinečnou adresu. Nechť máme proměnnou mojepromenna4b, která zabere v paměti 4 byty, tedy 4 buňky: Obrázek 4.1 Proměnná obsazující 4 paměťové buňky Ukazatele tak mají využití pro odkazování na proměnné podle adres. Jednoduše, deklarace ukazatele je de facto stejná jako proměnné, s rozdílem použití hvězdičky před identifikátorem (názvem) ukazatele. Tedy např. takto: int *ukazuj; Tím je překladači odlišeno, že je deklarován ukazatel a nikoli klasická proměnná. Protože je ukazatel číslo, lze na něj aplikovat matematické operace. Tomu se říká aritmetika ukazatelů resp. adresová aritmetika. K ukazateli můžeme přičíst konstantu a je možno spočítat rozdíl mezi dvěma ukazateli. Jak to funguje, si ukážeme na následujícím příkladu: int pole[3] = {1, 2, 3;
37 UKAZATELE, POLE, ŘETĚZCOVÝ A VÝPOČTOVÝ TYP int a = pole[0]; // v a bude 1 a = *(pole + 1); // v a bude 2 Aritmetika ukazatelů je užitečná zejména právě u operací s poli. Jsou povoleny pouze určité operace (aritmetické a relační) mezi ukazateli (Matoušek, 2016). Nechť máme ukazatele u, u1 a u2, dále x je celé číslo, pak jsou definovány následující operace aritmetiky ukazatelů: Tabulka 4.1 Aritmetické a relační operace mezi ukazateli operace výsledek význam u+x jako u výpočet nové adresy zvýšenou o x položek u-x jako u výpočet nové adresy sníženou o x položek u++ jako u výpočet nové adresy zvýšené o 1 u-- jako u výpočet nové adresy snížené o 1 u1-u2 int relativní vzdálenost ukazatelů u1==u2 bool zda ukazují oba ukazatele na stejnou proměnnou u1!=u2 bool zda ukazují oba ukazatele na jinou proměnnou u1>u2 bool zda má u1 vyšší adresu než u2 u1>=u2 bool Zda má u1 stejnou či vyšší adresu než u2 u1<u2 bool Zda má u1 nižší adresu než u2 u1<=u2 bool Má u1 stejnou či nižší adresu než u2 Jiné operace v aritmetice ukazatelů neexistují, například nelze ukazatele násobit. Ukážeme si nyní příklad praktického použití ukazatelů. Mějme pole typu int, které však nebude deklarováno staticky, ale jeho velikost zadá uživatel. Tedy pole bude mít dynamickou velikost dle zadání. A právě zde se uplatní použití ukazatele. Patrně byste již se svými zkušenostmi vymysleli něco podobného: #include <iostream> using namespace std;
PROGRAMOVÁNÍ V C++ 38 int main() { int velikostpole; cout << "Zadejte velikost pole: "; cin >> velikostpole; int dynapole[velikostpole]; Avšak při překladu by došlo k chybě. Upravíme poslední řádek a použijeme zde ukazatel a konstruktor new. #include <iostream> using namespace std; int main() { int velikostpole; cout << "Zadejte velikost pole: "; // vypis otazky k zadani cin >> velikostpole; int* dynapole = new int [velikostpole]; // pouziti ukazatele Vysvětleme si řádek int* dynapole = new int[velikostpole] V něm se deklaruje dynapole jako ukazatel na první prvek v poli. To znamená, že dynapole ukazuje na adresu indexu pole dynapole[0]. Poté již s pole lze pracovat, jako s jakýmkoli jiným. Například naplníme pole třetími mocninami od 0 do velikostpole. for (int n = 0; n < velikostpole; i++) mojepole[n] = n*n*n; Právě toto je typický případ, kdy je potřeba nepřímé odkazování pomocí ukazatele.
39 UKAZATELE, POLE, ŘETĚZCOVÝ A VÝPOČTOVÝ TYP Analogicky lze deklarovat ukazatel ukazující na jiný ukazatel a syntaxe bude taková: int **ukazatel Ukazatele mají v programování velký význam a přínos v řadě úloh. S jejich dalším využitím se může čtenář seznámit v uvedené literatuře. 4.2 Pole (array) Pole je velmi používanou strukturou s velmi širokým využitím. Patří mezi lineární datové struktury. Pomocí pole se implementují tzv. abstraktní datové struktury, mezi které patří fronta a zásobník. Obecně je pole struktura, obsahující konečný počet prvků, z nichž každý má jedinečný index. V praxi se obvykle setkáme pouze s jednorozměrným polem, které si lze představit takto: Obrázek 4.2 Obecná podoba jednorozměrného pole Toto pole má 4 prvky s indexem od 0 do 3. Pole může být číselné nebo nečíselné. Příklady polí: sudá čísla od 0 do 20 názvy 7 dnů v týdnu Někdy setkáme i s vícerozměrnými poli, například dvourozměrné je matice. Tato pole však nebudeme popisovat. 4.2.1 Programování polí v C++ Určitě Vás zajímá, jakým způsobem pracovat s polem. Podobně jako proměnnou či konstantu, musíme pole nejprve deklarovat. Deklarace pole v C++ může vypadat takto: int nazevpole[20]; Stejně jako proměnná či konstanta má i pole svůj datový typ, identifikátor a především uvedený rozsah (velikost), zde 20. To znamená, že toto pole bude obsahovat 20 prvků. Velikost může být v C++ zadána již deklarovanou konstantou, což v jazyce C přípustné není. Pole naplníme prvky podle indexů, tedy pro index 0 napíšeme
PROGRAMOVÁNÍ V C++ 40 nazevpole[0]=1; Plnit však takto pole by bylo příliš pracné po jednotlivých indexech. Pomocí cyklu for můžeme naplnit pole čísly pomocí inkrementace následovně: for (int i = 0; i < 20; i++) //cyklus s inkrementací i++ nazevpole[i] = i + 1; Pole však lze přímo inicializovat výpisem konkrétních prvků ve složených závorkách: string dnytydne = {'pondělí', 'úterý', 'středa', 'čtvrtek', 'pátek', 'sobota', 'neděle'; bez udání velikosti pole, bude mít tolik prvků, kolik uvedeme. Podívejme se nyní na kompletní program v C++, v němž deklarujeme pole s velikostí podle konstanty, naplníme toto pole a necháme vypsat obsah. Jsou zde použity dva cykly (viz kap. 9). #include <iostream> #include <string> using namespace std; int main(void) { const int delkapole = 50; // deklarace delky pole konstantou delkapole int mojepole[delkapole]; // deklarace pole s nazvem mojepole // cyklus pro naplneni pole for (int i = 0; i < delkapole; i++) mojepole[i] = i + 1; // cyklus pro vypis prvku pole funkci cout for (int i = 0; i < delkapole; i++) cout << mojepole[i] << ' '; return 0; //ukonceni programu s navratem 0
41 UKAZATELE, POLE, ŘETĚZCOVÝ A VÝPOČTOVÝ TYP V kapitole 9 se budeme zabývat podrobně cykly. Nicméně, tento cyklus s inkrementací postupně naplňuje, resp. vypisuje prvky pole od indexu i=0 po deklarovanou délku pole delkapole=50. 4.2.2 Vícerozměrná pole v C++ S vícerozměrnými poli se setkáme méně často, přesto fungují de facto stejně jako jednorozměrná. Dvourozměrné pole je matice rozměru m n a v C++ takovou matici (pole) definujeme takto: int matice[2][3] = {{1,2,4,{7,-5,14 nebo zjednodušeně int matice[2][3] = {1,2,4,7,-5,14 Zvláště u větších polí je vhodnější, i když pracnější, zápis se závorkami, které určují jednotlivé řádky matice. Každý prvek dvourozměrného pole pak má index pole2d[m][n] Příklad využití dvourozměrného pole mohou být například souřadnice na šachovnici unsigned char sachovnicexy[8][8]; Analogicky takto lze definovat a používat n-rozměrná pole. 4.3 Řetězcový typ string Zatímco datový typ char je určen pro jeden znak, string přináší možnost zapsat do jedné proměnné celý řetězec textu. Řetězcový typ je datový typ string, o němž bylo pojednáno v kapitole 2.2.2 o nečíselných datových typech. Typ string umožňuje definovat textové řetězce, s nimiž v programu následně pracujeme. Pro snadnější pochopení použití tohoto typu viz následující příklad. #include <iostream> #include <string>
PROGRAMOVÁNÍ V C++ 42 using namespace std; int main () { string ret1 = "Rumburak"; string ret2 = "Arabela"; string ret3; int delka; // copy ret1 into ret3 ret3 = ret1; cout << "ret3 : " << ret3 << endl; // spojeni (konkatenace) ret1 and ret2 ret3 = ret1 + ret2; cout << "ret1 + ret2 : " << str3 << endl; // delka retezce ret3 po spojeni delka = ret3.size(); cout << "ret3.size() : " << delka << endl; return 0; Na příkladu bylo ukázáno, jak lze spojovat řetězce konkatenací s operací + a zjišťovat jejich velikost (délku). 4.4 Výčtový typ enum Výčtové typy patří k tzv. návrhovým vzorům. Slouží k definování neprázdné, konečné množiny pojmenovaných hodnot. Každý výčtový typ musí být deklarován, jak už dobře víme, svým datovým
43 UKAZATELE, POLE, ŘETĚZCOVÝ A VÝPOČTOVÝ TYP typem a identifikátorem. Výčtové typy lze deklarovat pomocí klíčového slova enum (z angl. enumerated type) či pomocí typedef. Ukážeme si obě možnosti a zmíníme, která má v čem své přednosti a nevýhody. Výčtový typ tak tvoří množinu definovaných konstant. Výčtové typy poté lze v programu opakovaně využívat jako návrhový vzor, takže máme připraveny výčtové typy pro opakované použití. Někdy potřebujeme proměnnou, která může nabývat pouze určitých hodnot. Mějme například proměnnou graf, která může nabývat hodnot pruhovy, sloupcovy, kolacovy a prostorovy. Proto existují výčtové seznamy, které proměnné vymezují hodnoty. Definice výčtového typu je snadná: enum nazev {hodnoty deklarace; což by v případě příkladu s grafy vypadalo takto enum grafy {pruhovy,sloupcovy,kolacovy,prostorovy graf; Představme si jiný výčtový typ roční období. Zavedeme výčtový seznam následovně: enum obdobi {jaro, leto, podzim, zima a posléze lze přiřadit jednotlivým obdobím hodnotu, například podle čísla měsíce, v němž období kalendářně začíná: enum obdobi { jaro = 3; leto = 6; podzim = 9; zima = 12; ; Pomocí výčtových typů tak můžeme jednoduše definovat hodnoty proměnných s pouze určitým výčtem povolených hodnot a sdružujeme je do společného názvu typu.
PROGRAMOVÁNÍ V C++ 44 4.5 Praktický příklad s komentářem Opět na konec kapitoly uveďme souhrnný příklad, v němž jsou v C++ naprogramovány probrané pojmy. Příklad je zaměřen na využití výčtového typu. #include <iostream> using namespace std; int main() { enum Dny {Pondeli, Utery, Streda, Ctvrtek, Patek, Sobota, Nedele; Dny dnes; dnes = Streda; if ( dnes == Sobota dnes == Nedele ) cout << "O vikendu se nepracuje!\n"; else cout << "Dnes je pracovni den.\n"; return 0; Důležité zde je to, že zadáme-li do proměnné dnes cokoli jiného než z výčtu Dny, pak program vrátí chybu, že tento název není z povoleného výčtu. Přesvědčme se o tom z obrázku níže.
45 UKAZATELE, POLE, ŘETĚZCOVÝ A VÝPOČTOVÝ TYP Obrázek 4.1 Nepovolená hodnota výčtového typu končí chybou Mnohé z použitých fragmentů kódu si podrobně rozebereme v dalších kapitolách, zj. Využití podmínek. Je však na tomto příkladu vidět, jak můžeme výčtem povolit pouze určité proměnné, s nimiž bude možno v programu pracovat na základě definovaného výčtu. V této kapitole jsou vysvětleny pojmy ukazatel, pole a řetězcový a výčtový typ. Pole patří mezi základní lineární datové struktury se širokým použitím v praxi včetně implementace abstraktních datových struktur seznam, fronta a zásobník. 1. Vysvětlete stručně princip a využití ukazatelů. 2. Uveďte příklad pole a k čemu pole slouží. Jaké abstraktní datové struktury programujeme pomocí pole? 3. K čemu slouží řetězcový a výčtový typ? Vysvětlete jednoduše jejich použití. Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5.
Kapitola 5 Funkce a procedury Po prostudování kapitoly budete umět: použít standardní funkce v C++ a rozumět direktivě #include; definovat vlastní funkce v programu; využít tzv. procedury jako funkce s návratovým typem void. Klíčová slova: Funkce, procedury, #include, direktivy, tělo funkce, cmath, void.
47 FUNKCE A PROCEDURY 5.1 Jádro procedurálního programování Jelikož se v těchto oporách zabýváme procedurálním programováním v C++, pojem procedura je jedním ze stěžejních. A stejně tak pojem funkce. Právě tyto dva pojmy tvoří de facto jádro procedurálního programování. 5.2 Funkce Mezi standardní funkce jazyka C++ patří například ty, které jsou součástí knihovny cstdio. Patří sem funkce vstupu/výstupu (I/O), pro práci se soubory, matematické funkce a další. Velké množství funkcí poskytuje knihovna cmath. Najdeme zde goniometrické funkce, zaokrouhlování, základní statistické funkce, apod. Pokud je budeme chtít využívat, musíme použít knihovnu zařadit mezi direktivy na začátku, tedy #include <cmath> Pak nic nebrání například výpočtu odmocnin nebo absolutní hodnoty. S těmito funkcemi si však nevystačíme a proto je základem umět používat vlastní funkce. Obecná definice funkce v C++ vypadá takto: navratovy_typ identifikator ([parametry,...]) { telo funkce Návratový typ je datový typ, jehož výstup funkce vrací. Identifikátor je název funkce a následují parametry, které nejsou povinné. V bloku pak programujeme tělo funkce, tedy co funkce dělá. Následně ji v programu zavoláme. Parametry určují, jaká data funkce vrací. Pokud nemá funkce vracet žádná data, jako parametr uvedeme klíčové slovo void. Každá funkce musí mít návratový datový typ, funkce main má vždy int. Funkci lze kdykoliv ukončit pomocí příkazu return, za nějž se napíše hodnota či výraz, jehož výsledek se stane návratovou hodnotou funkce. Jestliže funkce žádnou návratovou hodnotu nemá, uvádí se
PROGRAMOVÁNÍ V C++ 48 return bez hodnoty nebo výrazu, pouze ukončeno středníkem. Pokud funkce má návratovou hodnotu, je použití return na konci funkce povinné a za return musí být výraz, jenž po vyhodnocení musí mít tentýž datový typ jako datový typ návratové hodnoty. Podívejme se, jak v praxi v C++ zapíšeme funkci pro výpočet druhé mocniny zadaného čísla x, tedy x2. int nadruhou (int x); // parametrem je cislo x { // funkce vraci hodnotu x*x return x*x; Funkci nadruhou bychom poté v programu zavolali nadruhou(x); a nechali vypsat hodnotu. Samozřejmě proměnná x musí být deklarována. Podobně například můžeme vytvořit funkci, která vypíše číslo a, pokud je splněna podmínka, že a<b. int mensicislo(int a, int b) { if (a<b) return a; // funkce vraci číslo a pokud a<b return b; // vraci číslo b Tvorba funkce v C++ se tak sestává z těchto kroků ze zadání datového typu, názvu a parametrů a tělo funkce, tedy vykonávané příkazy. Často rozlišujeme definici a deklaraci funkce jako samostatné kroky. Funkci lze pouze deklarovat s příslušným datovým typem, názvem a parametry, ale samotnou definici v těle funkce můžeme zapsat až později. Příklad: float vypocty(int, float, float *); /* pouze deklarace funkce */ int main(void)
49 FUNKCE A PROCEDURY { float a, b; a = vypocty(5, (float) 0.3, &b); printf("soucet = %5.2f\nNasobek = %5.2f\n", a, b); return 0; float vypocty(int a, float b, float *c) // zde teprve definujeme, co funkce vypocty provadi { float f; *c = (float) a*b; f = (float) a+b; b = 10.6; /* menime lokalni promennou b, nema s promennou b ve funkci main nic spolecneho */ return f; Jednoduchý příklad využití matematické funkce log10() z knihovny cmath: #include <iostream> #include <cmath> using namespace std; int main() { cout<<log10(200); // pouze vypise hodnotu dekadickeho logaritmu cisla 200
PROGRAMOVÁNÍ V C++ 50 Některé, nejen matematické funkce, které v C++ lze využívat, najdete např. na webu Programming.com 14. Funkce tak představují jeden ze stěžejních prvků programování v C++ a de facto i v dalších programovacích jazycích. 5.3 Procedury Procedura je vlastně zvláštním případem funkce, která má návratový typ void a nemusí mít žádné parametry. Deklarace takové funkce je void nazev_procedury(). De facto tak pojem procedura v C++ nepoužíváme. Příklad procedury, tedy funkce nevracející žádnou hodnotu: void Cislo(int cislo){ cout << "Predane cislo je: " << cislo << endl; K procedurám není dále potřeba rozebírat nic dalšího, jde pouze o funkci, která nevrací hodnotu, ale například provede výpis na obrazovku. 14 https://www.cprogramming.com/function.html
51 FUNKCE A PROCEDURY Funkce patří ke stěžejním tématům procedurálního programování. V kapitole bylo vysvětleno, co funkce je, jak je deklarujeme a definujeme a co musí každá funkce obsahovat. Zvláštní funkcí je bezparametrická funkce main(), kterou musí každý program v C++ začínat. Funkce bez návratové hodnoty je procedura, nemusí obsahovat ani žádné parametry. Avšak pojem procedura není v C++ definován. Chceme-li využívat funkce z knihoven C++, například matematické funkce, je třeba knihovnu zadat do direktivy #include. 1. Co je funkce a procedura? Jaké mohou být návratové typy funkcí? 2. K čemu slouží bezparametrická funkce main()? 3. jakým způsobem můžeme využívat funkce z knihoven C++ pomocí direktivy #include? Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5.
Kapitola 6 Uživatelské vstupy Po prostudování kapitoly budete umět: porozumět rozdílu funkcí cin/cout vs. prinft/scanf; aplikovat základní formátovací řetězce pro uživatelské vstupy/výstupy; naprogramovat jednoduché programy s využitím I/O funkcí. Klíčová slova: Vstupy, výstupy, printf, scanf, I/O, cin, cout, standardní vstup, formátovací řetězce.
53 UŽIVATELSKÉ VSTUPY V této kapitole se budeme zabývat tím, jak pracovat s uživatelskými vstupy a výstupy s formátováním. Doposud jsme se pro vstup a výstup setkali pouze se standardními funkcemi cin, resp. cout. Nyní se podíváme na funkce printf a scanf, umožňující definovat formátování vstupu, resp. výstupu. Abychom tyto funkce mohli využívat, je nutné zavést knihovnu cstdio, tedy direktivou #include <cstdio>. Funkce printf a scanf patří mezi vstupně-výstupní (input-output; I/O) standardní funkce v C++. Zcela stručně na začátek: Funkce printf se používá pro formátovaný výstup a funkce scanf pro formátovaný vstup (obvykle z klávesnice). Syntaxe funkce printf je na první pohled složitá, ale má své pevně dané části své struktury. V obecnosti lze syntaxi funkce printf zapsat takto: int printf(const char *format,...); Funkce má tzv. formátovací řetězec a další, volitelné parametry. Velmi důležité jsou formátovací parametry funkce. Ty totiž stanovují formátování výstupu. Kolik je parametrů, jakého jsou typu a jak a kam se mají vypsat, se uvádí ve formátovacím řetězci. Řídícím znakem je '%', pokud jej chcete vypsat, musí se zdvojit. Přehled vybraných, často používaných sekvencí pro formátovací řetězec funkce printf (platí i pro scanf): znak i význam pro formát číslo integer o, u, x, X unsigned, nezáporné celé číslo v osmičkové, desítkové, šestnáctkové soustavě c Int převedeno na char s jedním znakem e, f double (exponenciálně, formát s des. tečkou) p s void *, číslo v hexadecimální soust., paměť řetězec string Několik praktických příkladů přímo v kódu C++ s funkcí printf: /* 1 */ printf("rovnice: 2 = %i\n", 2); /* 2 cislo v hexadecimalni soustave */ printf("255 = %#x = %x\n", 255, 255);
PROGRAMOVÁNÍ V C++ 54 /* 3 */ printf("1 = %i = %10i = % 10i = %-10i = %010i\n", 1, 1, 1, 1, 1); /* 4 */ printf("%+i = %i, %+i = %i\n", 1, 1, -1, -1); /* 5 */ printf("%f, %.0f, %#.0f\n", 8.3, 9.7, 5.4); /* 6 useknuty text na 5 znaku */ printf("toto je useknutý text: %-10.*s.\n", 5, "useknutý text"); return 0; Výstupem bude těchto řádků: Rovnice: 2 = 2 255 = 0xff = ff 1 = 1 = 1 = 1 = 1 = 0000000001 +1 = 1, -1 = -1 8.300000, 10, 5. Toto je useknutý text: usekn. Podrobné detaily o formátovacích řetězcích a volitelných parametrech funkce printf lze najít v referenčním manuálu 15. Funkce scanf je velmi podobná včetně podobné syntaxe s formátovacím řetězcem a parametry. Následující příklad ukazuje možnosti využití této funkce společně s funkcí printf. #include <cstdio> int main () { 15 https://linux.die.net/man/3/printf
55 UŽIVATELSKÉ VSTUPY char str [100]; int i; printf ("Napiste sve jmeno: "); scanf ("%79s",str); printf ("Napiste svuj vek: "); scanf ("%d",&i); printf ("Mr. %s, %d years old.\n",str,i); printf ("Zadejte číslo v hexa soustave: "); scanf ("%x",&i); printf ("Zadali jste %#x (%d).\n",i,i); return 0; Ačkoli formátovací řetězce a parametry jsou na první pohled složité, díky těmto funkcím můžete používat pokročilé možnosti formátování vstupu a výstupu oproti standardním funkcím cin a cout.
PROGRAMOVÁNÍ V C++ 56 V jazyce C++ lze využívat uživatelské vstupy a výstupy pomocí funkcí printf a scanf. Jsou bohaté na možnosti přizpůsobení formátu zobrazení a čtení. De facto jsou pokročilejší alternativou ke standardním funkcím cin a cout, s nimiž jste se již seznámili. 1. Kterou knihovnu musíme zavést pro využití uživatelských vstupů? 2. Stručně vysvětlete použití funkcí printf a scanf. 3. Co určuje formátovací řetězec a který znak je stěžejní? Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5.
Kapitola 7 Typy struct, union, typedef a alokace paměti Po prostudování kapitoly budete umět: rozlišit složené datové struktury struct, union; nadefinovat vlastní datový typ pomocí typedef; použít základní funkce pro alokaci a dealokaci paměti v C++. Klíčová slova: Složené datové typy, objektové datové typy, unie, union, struct, typedef, alokace paměti.
PROGRAMOVÁNÍ V C++ 58 Struktury (struct) a unie (union) jsou dva podobné datové typy a mají blízko k OOP. Obě si vysvětlíme. 7.1 Struktury struct Struct definuje strukturu, de facto objekt s vlastnostmi. Podobně jako pole shromažďuje hodnoty stejného typu, struktury shromažďují data různých typů. Příkladem může být jednoduchá "databáze" osob, u kterých uvádíme jméno, věk, hmotnost a výšku v cm. struct Osoby { char jmeno[40]; // číslo 40 omezuje maximalni delku int vek; float hmotnost; int vyska; osoba; Takto můžeme definovat struktury podobně jako jednoduché databáze, resp. jako objekty v OOP, i když paradigma OOP pracuje ještě jinak. Následně přiřadíme hodnoty osoba.jmeno= Ondra ; osoba.vek=52; osoba.hmotnost=69.8; osoba.vyska=183; Poté lze s jednotlivými proměnnými struktury pracovat jako s běžnými proměnnými. Smysl je ve sdružení proměnných různých typů do logického celku, tedy struktury.
59 TYPY STRUCT, UNION, TYPEDEF A ALOKACE PAMĚTI 7.2 Unie, union Unie, angl. union má velmi podobný princip jako struktura struct. S uniemi se však nesetkáme často. Mají význam při programování nízkoúrovňového kódu. Zásadní rozdíl oproti struct je to, že v paměti je aktuálně vždy jen jeden člen unie. Segment kódu pro unii vypadá například takto: union CisloPismeno { int cislo; char pismeno; unie; a pokud napíšeme např. unie.cislo = 580; bude v paměti unie číslo 580 a nic více. Narozdíl od struktury struct, v unii všechny položky sdílejí pouze jedno paměťové místo a tudíž se překrývají. V případě unie je místo alokováno pouze pro největší položku. Datový typ struct i union patří mezi objektové datové typy a zároveň mezi tzv. složené datové typy. K objektovým datovým typům a vůbec základnímu principu OOP patří třída (class). Jelikož se v tomto textu nezabýváme OOP, nebudeme dále pojem třída vysvětlovat. Zájemce o OOP odkazujeme rovnou na vhodnou literaturu, např. (Matoušek, 2011). Další možností je vytvořit si zcela vlastní datový typ, viz další podkapitola. 7.3 Uživatelský datový typ Přestože pro většinu programátorských úloh si vystačíme s vestavěnými, standardními datovými typy (int, float, enum, apod.), nechybí v jazyce C++ možnost vytvoření uživatelského datového typu. Klíčovým slovem je typedef (type definition) a základní deklarace je: typedef definice_typu identifikátor; a v praxi to může vypadat například následovně: typedef signed char malyint; tímto jsme definovali datový typ s názvem malyint.
PROGRAMOVÁNÍ V C++ 60 7.4 Dynamická alokace paměti V jazyce C++ máme několik možností, jak dynamicky alokovat paměť. Základní funkce vycházejí z jazyka C a přidává několik nových. Dynamická alokace paměti umožňuje alokovat potřebnou paměť pro proměnné dle aktuální potřeby, což umožňuje efektivnější využití paměti. To znamená, že můžeme dynamicky alokovat a dealokovat paměť jednotlivým proměnným, které nejvíce potřebujeme. V jazyce C++ jsou oproti jazyku C k dispozici nové způsoby alokace paměti. Ti, kteří znají jazyk C, budou pravděpodobně znát možnosti alokace a dealokace paměti pomocí funkcí malloc a free. V jazyce C++ se nabízí použití operátoru new. Naopak dealokace paměti pomocí new se provede operátorem delete, který je unární a má jediný operand název ukazatele dané proměnné, kterou hodláme z paměti uvolnit. Příkladem budiž následující fragment kódu: pole = new int[50]; // alokace paměti pro pole s 50 prvky V této chvíli bude pro pole alokována paměť. Dealokaci provedeme takto: delete [] pole; V případě jednoduché proměnné stačí napsat delete promenna; Dynamickou alokaci využijeme především u rozsáhlejších úloh, kde chceme co nejefektivněji paměť využívat.
61 TYPY STRUCT, UNION, TYPEDEF A ALOKACE PAMĚTI Datové struktury struct a union patří mezi pokročilejší, avšak hojně využívané datové typy v C++. Pomocí definice struktury struct lze vytvářet uživatelské strukturované typy vlastností. Unie se nepoužívají tak často, přesto mají svůj význam zj. v nízkoúrovňovém kódu. Další možností je definovat uživatelské datové typy pomocí typedef. Rovněž se tato kapitola věnuje základům alokace paměti v C++ 1. Co je funkce a procedura? Jaké mohou být návratové typy funkcí? 2. K čemu slouží bezparametrická funkce main()? 3. jakým způsobem můžeme využívat funkce z knihoven C++ pomocí direktivy #include? Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5.
Kapitola 8 Podmínky, přepínač switch-case Po prostudování kapitoly budete umět: používat podmínky a rozumět jejich využití; naprogramovat podmínky if a přepínač switch-case v C++; využívat složené podmínky s logickými operátory. Klíčová slova: Operátory, operátory aritmetické, priorita operátorů, operátory logické, operátory bitové, relační operátory, přetěžování operátorů.
63 PODMÍNKY, PŘEPÍNAČ SWITCH-CASE Podmínky patří mezi zcela zásadní části programů. Umět naprogramovat podmínku není pouze o znalosti zápisu ve zdrojovém kódu, v našem případě v jazyce C++, ale zejména pochopit princip podmínek a cyklů z pohledu návrhu samotného algoritmu. V programátorské praxi se pro algoritmy používají především dva typy podmínek. Obojí si v této kapitole vysvětlíme, samozřejmě na konkrétních příkladech v C++. Použití podmínek umožňuje tzv. větvení algoritmu 16. To znamená, že v určitém kroku algoritmus provede jeden z více příkazů na základě splnění nebo nesplnění určité podmínky. Samozřejmě nemusí to být jeden příkaz, ale celý blok. Obecně tak lze podmínku definovat takto: Pokud nastane něco, udělej tohle a pokud ne, udělej něco jiného. Formálně se jedná de facto o rozhodovací pravidla IF-THEN-ELSE. Z čehož je také dán název základní a nejčastěji využívané podmínky if, kterou lze použít v takřka všech programovacích jazycích. Obrázek 8.1 Větvení algoritmu splnění nebo nesplnění podmínky S podmínkami se běžně setkáváme v mnohých, nejen např. Matematických algoritmech. Určitě znáte z hodin matematiky postup řešení kvadratické rovnice, kdy srovnáváme, zda je diskriminant D < 0, D = 0 či D > 0 a dle toho se stanoví počet kořenů rovnice. Možná jste někdy též použili podmínky v tabulkovém procesoru, v němž je funkce IF (KDYŽ). A přesně to je případ podmínky if, kterou využijeme při programování. 8.1 Podmínka if V jazyce C++ je použití podmínek jednoduché. Obecná syntaxe podmínky je zapsána: if (podminka) { prikazy 16 http://mathweb.wz.cz/algoritmy/teorie.htm
PROGRAMOVÁNÍ V C++ 64 // pokud podminka neni splnena else { prikazy Mějme v programu následující situaci. Jestliže je číslo x kladné, vypíše se hláška, že je kladné a naopak pokud je záporné, vypíše se hláška, že je záporné. Podmínku if použijeme následovně: if (x>0) // pokud plati nerovnost x>0 cout << "Cislo je kladne."; else cout << "Cislo je zaporne."; Jak je vidět, právě u podmínek se používají relační operátory, o nichž byla řeč v kapitole o operátorech. Podmínky jsou konstruovány na využití relačních operátorů pro definici podmínky. V příkladu byly vynechány blokové závorky, jelikož je příkaz (funkce cout) pouze jeden. Je-li však v podmínce více příkazů, je nutno blokové závorky použít. Naprogramujme si další podmínku. Zadání zní takto: Uživatel zadá číslo, z něhož se vypíše druhá odmocnina. Jelikož je v oboru R definována odmocnina je z kladných čísel, podmínkou bude, že číslo musí být >=0. Kód v C++ bude následující: #include <iostream> #include <cmath> using namespace std; int main() { cout << "Zadejte číslo, z nehoz chcete vypočítat druhou odmocninu:" << endl; int a; cin >> a;
65 PODMÍNKY, PŘEPÍNAČ SWITCH-CASE if (a >= 0) { double o = sqrt(double(a)); // pouziti pretypovani cout << "Odmocnina z cisla " << a << " je " << o << endl; else cout << "Zaporne číslo nelze odmocnit." << endl; cin.get(); cin.get(); return 0; K tomu, abychom mohli používat matematickou funkci sqrt(), je nutné zavést direktivou #include knihovnu math. Použití klíčového slova else v tomto případě není nutné, jelikož testujeme pouze dvě možné situace. Blok pro if obsahuje příkazy při splnění podmínky, jakmile se blok ukončí, program automaticky předpokládá, že podmínka splněna není, resp. byl ukončen blok podmínky a následují příkazy, kdy podmínka neplatí. 8.1.1 Logické operátory v podmínkách Kromě použití relačních operátorů se v podmínkách velmi často setkáváme s využitím logických operátorů. Byla o nich zmínka v kapitole 3.4 s uvedením, že se aplikují často u podmínek. A jsme právě u toho. Logické neboli booleovské operátory nám umožňují zadávat složené podmínky z několika dílčích podmínek. Opět si to ukážeme na praktickém příkladu v C++. Zadání je, že uživatel má zadat číslo z intervalu od 5 do 10 nebo z intervalu od 250 do 300 a na základě vstupu se vypíše hláška, zda uživatel zadal správné číslo či nikoli. Právě tím, že jsou intervaly dva, použijeme booleovský operátor pro nebo. Takže daná podmínka bude vypadat: cout << "Zadejte cislo z intervalu 5-10 NEBO 250-300:" << endl; int cislo;
PROGRAMOVÁNÍ V C++ 66 cin >> cislo; if (((cislo >= 5) && (cislo <= 10)) ((cislo >=250) && (a <= 300))) cout << "Zadali jste vyhovujici cislo." << endl; else cout << "Zadali jste nevyhovujici cislo." << endl; cin.get(); cin.get(); Detailně se podívejme na řádek s podmínkou, tedy if (((cislo >= 5) && (cislo <= 10)) ((cislo >=250) && (a <= 300))) Tento na první pohled složitý zápis podmínky však přesně reflektuje naše zadání: if ((cislo >= 5) && (cislo <= 10)) zadané číslo má být z intervalu 5 až 10, tedy zápis toho, že platí zároveň, že cislo>=5 a cislo<=10 logický operátor nebo pro zadání toho, že bude platit první podmínka nebo ta další ((cislo >=250) && (a <= 300)) zadané číslo má být z intervalu 5 až 10, tedy zápis toho, že platí zároveň, že cislo>=250 a cislo<=300 Tedy, buď je číslo z prvního, nebo z druhého intervalu. Tímto způsobem lze zapisovat i velmi složité podmínky. Pokud by logické operátory nebyly, museli bychom pro každou ze čtyř podmínek napsat if. Použití logických operátorů značně zjednodušuje zápis složených podmínek. 8.1.2 Varianty podmínky if Podmínku if lze využívat ve více variantách dle toho, kolik je potřeba testovat variant pro podmínku. základní if určeno pro podmínku, kdy se vykoná příkaz, platí-li podmínka v if if-else pro vytvoření druhé rozhodovací větve (lze použít stejně jako základní if) else if pro vytvoření mnoha rozhodovacích větví podmínky Ukažme si praktické použití else if. Tato konstrukce se sestává ze základní podmínky if a poté dalších variant. byte dentydne; cout << "Zadejte cislo dne (1-7): "; cin >> dentydne;
67 PODMÍNKY, PŘEPÍNAČ SWITCH-CASE // ted se budou testovat jednotliva cisla dnu pomoci else if if (dentydne == 1) // první podminka cout << "Pondeli" << endl; else if(dentydne == 2) cout << "Utery" << endl; else if(dentydne == 3) cout << "Streda" << endl; else if(dentydne == 4) cout << "Ctvrtek" << endl; else if(dentydne == 5) cout << "Patek" << endl; else if(dentydne == 6) cout << "Sobota" << endl; else if(dentydne == 7) cout << "Nedele" << endl; else // zadano cokoli jineho cout << "Zadano cislo mimo interval!" << endl; Tato podmínka vypisuje název dne týdne podle zadaného odpovídajícího čísla dne. Pomocí cyklu else if testujeme sedm možných variant a na konci je varianta pro chybovou hlášku. Cyklus else if musí končit samotným else, tím překladač pochopí, že zde cyklus končí. Místo zápisu podmínek else if lze v těchto případech použít tzv. přepínač switch, o němž je následující podkapitola.
PROGRAMOVÁNÍ V C++ 68 8.2 Přepínač switch-case Jiným typem podmínky je použití přepínače switch-case. Konstrukce je převzata z jazyka C. Je zjednodušením pro podmínky else-if. Podívejme se na příklad použití konstrukce switch-case. int stisk_klavesy; stisk_klavesy = getch(); // nacteni jednoho znaku z klavesnice switch(stisk_klavesy) { case 'a' : printf("byla stisknuta klavesa a."); break; case 'b' : printf("byla stisknuta klavesa b."); break; case 'c' : printf("byla stisknuta klavesa c."); break; default : printf("byla stisknuta jina klavesa."); break; Příklad ukazuje, že se vypíše hláška dle toho, jaká klávesa byla stisknuta. Důležité je použití default, pokud neplatí ani jeden případ case. Od toho je odvozen i název switch-case, což lze volně přeložit jako přepni podle případu. Příkazy break je nutno uvádět, v opačném případě by totiž program po provedení příkazů aktuálního bloku přešel na blok následující, a pokud by ani ten neobsahoval kód zajišťující opuštění switche, vykonával by program postupně i následující větve switche.
69 PODMÍNKY, PŘEPÍNAČ SWITCH-CASE 8.3 Zachycení a ošetření výjimek S podmínkami souvisí také ošetřování výjimek, angl. exception handling. Jde o to, že v programu chceme zachytit výjimky, které mohou v určité situaci nastat. V jazyce C++ se k tomu používá konstrukce try-catch. Obecná syntaxe je try { // Nejaky kod, ktery muze zpusobit vyjimku catch (Typ vyjimky) { // Kod, ktery se provede pri vyvolani vyjimky Jeden ze základních případů je například dělení nulou. Napište tento program: int main() { double a, b; cin >> a; cin >> b; try { if (b == 0) // podminka pro vyjimku throw "Nelze delit nulou.\n"; cout << a / b << endl; catch (const char* exception)
PROGRAMOVÁNÍ V C++ 70 { cout << "Byla zachycena vyjimka - " << exception; return 0; V tomto jednoduchém případě by nebylo nutné použít ošetření výjimek, stačila by pouze podmínka, která v případě, že b = 0, vypsala chybové hlášení. Je to však ukázka principu ošetřování výjimek. Bloků catch může být několik, jelikož v řadě algoritmů může docházet k různým výjimkám, nejen k jediné. Opět bychom mohli použít například cyklus else if. Použití zachycení výjimek uplatníte spíše u náročnějších úloh, kde chceme předcházet neočekávaným ukončením programu z důvodu synchronní výjimky. Můžete se však detailněji seznámit s principem výjimek a jejich handlerů 17. Podmínky patří k základním částem mnoha programů, kdy je potřeba algoritmus větvit na základě splnění podmínky. V této kapitole bylo vysvětleno použití podmínek v různých situacích. Základní a nejpoužívanější podmínkou je if, avšak taktéž tzv. přepínač switch má rozhodně svůj význam v mnoha aplikacích. Na příkladech bylo ukázáno, jak v C++ podmínky programovat. Podmínky se neobejdou bez využití relačních i logických operátorů. 1. Co znamená větvení algoritmu? 2. Jaké dva druhy podmínek můžeme v C++ používat? V čem se liší principem? 3. Jednoduše vysvětlete princip přepínače switch. Uveďte příklad jeho použití. Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5. 17 https://www.fi.muni.cz/~xbayer/pb161/2007/10/10_xbayer_01.pdf
Kapitola 9 Cykly for, while, do-while Po prostudování kapitoly budete umět: vysvětlit použití cyklů a rozdíly mezi s předem známým a neznám počtem opakování; naprogramovat jednoduché cykly pro konkrétní příklady; rozhodnout, který typ cyklu je pro danou úlohu lepší a proč. Klíčová slova: Cykly, for, while, do-while, cyklus, opakování.
PROGRAMOVÁNÍ V C++ 72 Cykly jsou při programování velmi silným nástrojem pro opakované činnosti. Jejich použití významně zjednodušuje programování opakovaných činností. V jazyce C++ můžeme využívat cykly for, while a do-while. Vysvětlíme si je podrobně na konkrétních příkladech. Na začátku si řekněme, že rozlišujeme cykly s předem známým počtem opakování a s předem neznámým počtem opakování. Mohli bychom říci, že u cyklů s předem neznámým počtem opakování uplatňujeme princip podmínek, jelikož se cyklus zadává formou dokud platí něco, opakuj příkazy. 9.1 Cyklus for Nepochybně základním a nejpoužívanějším cyklem je for. Je to cyklus s předem daným počtem opakování, jehož syntaxe je zjednodušeně pro x od i do j opakuj tyto kroky. V obecném pseudokódu 18 se cyklus for formuluje takto: for x=m to n do { prikazy vykonane v cyklu Obecně lze strukturu cyklu for v C++ zapsat takto: for (pocatecni_podminka; podminka; iteracni_krok) { telo cyklu Tělo cyklu jsou příkazy, které se vykonávají po dobu platnosti podmínky pro cyklus. V praxi bychom mohli například nechat vypsat pro proměnnou j funkcí printf nějaký text. for (j=0; j<20; j++) { printf( Ja jsem cyklus for! ); První výraz v závorce nastaví počáteční hodnotu proměnné, která se používá ke kontrole, zda má již cyklus skončit. Další výraz je podmínka a poslední je příkaz k navýšení či zmenšení hodnoty proměnné, kterou jsme nastavili v první části. Po každém průchodu se navýší hodnota iteračního kroku. V příkladu výše proběhne cyklus 20 a tolikrát se vytiskne text z funkce printf. Počátečním výrazem 18 https://it-slovnik.cz/pojem/pseudokod
73 CYKLY FOR, WHILE, DO-WHILE je přiřazení, které vynuluje naši řídící proměnnou. Poté se testuje, zda platí podmínka to je předpoklad k dalšímu provedení cyklu. Nakonec se tedy řídící proměnná navýší o 1 (jde o operátor inkrementace ++). Vše takto probíhá, dokud j se nerovná 19, potom cyklus skončí. Zkusme něco praktičtějšího. Cyklus for lze použít pro naplnění pole. Mějme pole, jehož 100 prvků chceme naplnit nulami. Dělat to ručně by bylo hodně zdlouhavé, efektivně využijeme cyklus for následovně: int x, novepole[n]; for (x=0; x<=(n-1);x++) pole[x]=0; A dojde k naplnění všech prvků pole nulami. Proč je v cyklu x <= (n-1) a nikoli až x <= n? Jelikož první index pole je 0, tudíž pro počet prvků n = 100 jsou indexy od 0 do 99. 9.2 Cyklus while a cyklus do-while Dalším typem cyklu je while a do-while. Cykly while a do-while jsou si svým principem podobné. Hlavní rozdíl je v umístění zápisu podmínky. Oba cykly jsou s předem neznámým počtem opakování, jelikož jde o cyklus typu dělej něco, dokud platí podmínka. while (j<20) { printf( Ja jsem cyklus while! ); // vypise se dokud plati j<20 Oproti tomu do-while je s podmínkou na konci: do { printf( Ja jsem cyklus do-while! ); while (j<20) De facto jsou tyto cykly vzájemně nahraditelné a ve většině případů záleží pouze na zvyku programátora, zda použije while či do-while.
PROGRAMOVÁNÍ V C++ 74 9.3 Praktický příklad s komentářem V této chvíli už byste měli mít znalosti základních prvků, abyste si mohli naprogramovat ucelenější program. Následující kód je jednoduchá kalkulačka 19, která vykonává základní aritmetické operace se dvěma zadanými čísly. #include <iostream> #include <string> using namespace std; int main() { cout << "Bezva kalkulačka v C++" << endl; string pokracovat = "ano"; while (pokracovat == "ano") { cout << "Zadejte prvni cislo:" << endl; float a; cin >> a; cout << "Zadejte druhe cislo:" << endl; float b; cin >> b; cout << "Co chcete s cisly provest za operaci?" << endl; cout << "1 - scitani" << endl; cout << "2 - odcitani" << endl; 19 https://www.itnetwork.cz/cplusplus/kurz/zaklady/zdrojove-kody/c-plus-plus-tutorial-osetreni-uzivatelskych-vstupu/
75 CYKLY FOR, WHILE, DO-WHILE cout << "3 - nasobeni" << endl; cout << "4 - deleni" << endl; int volba; cin >> volba; float vysledek = 0.0f; switch(volba) // volby operace { case 1: vysledek = a + b; break; case 2: vysledek = a - b; break; case 3: vysledek = a * b; break; case 4: vysledek = a / b; break; if ((volba > 0) && (volba < 5)) // platne volby 1 az 4 z case cout << "Vysledek je: " << vysledek << endl; else cout << "Neplatna volba, zadejte cislo 1 az 4" << endl; cout << "Chcete zadat dalsi priklad? [ano/ne] - stiknete klavesu A nebo N" << endl;
PROGRAMOVÁNÍ V C++ 76 cin >> pokracovat; cout << "To je vse, aplikaci ukoncite libovolnou klavesou." << endl; cin.get(); cin.get(); V programu jsou využity podmínky, výstupy na obrazovku, použití datového typu string a další prvky, s nimiž jste se již seznámili. Proto by neměl být problém kódu porozumět a zvládnout jej modifikovat. Používání cyklů patří k základním dovednostem programátora. Bez cyklu Používání cyklů patří k základním dovednostem programátora. Bez cyklu se obejde málokterý program. Princip cyklu je v opakování bloku příkazů při splnění určité podmínky. Rozlišujeme cykly s předem daným počtem opakování, to je v jazyce C++ (a obecně) cyklus for a cykly, u nichž neznáme předem, kolikrát se bude tělo cyklu opakovat a pouze zadáme podmínku platnosti vykonávání cyklu. K těmto účelům nabízí C++ cykly while a do-while, které jsou si podobné a liší se pouze zápisem podmínky na začátku, resp. na konci. Cykly lze v určitých případech nahradit rekurzí, viz kap. 11. 1. Vysvětlete základní rozdíl mezi cyklem for a while. 2. Jednoduše popište syntaxi zápisu cyklu for. 3. Čím se od sebe liší cykly while a do-while? Jsou mezi sebou nahraditelné? Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5.
Kapitola 10 Práce se soubory v C++ Po prostudování kapitoly budete umět: porozumět principu datových proudů; použít základní funkce fopen, fclose, porozumět režimům práce se soubory; naprogramovat jednoduché algoritmy pro práci s textovými soubory. Klíčová slova: Soubory, datové proudy, fopen, fclose.
PROGRAMOVÁNÍ V C++ 78 Nezřídka potřebujeme v programu pracovat s externími soubory. V rámci této kapitole se omezíme na práci s textovými soubory, které v programu budeme zpracovávat. Mezi základní činnosti patří otevření (načtení) souboru a zpracování textových dat v něm obsažených. Pro práci se soubory budeme využívat knihovnu cstdio, zavedeme ji již známým způsobem direktivou #include <cstdio>. Z této knihovny využijeme několik základních funkcí pro zpracování textových souborů. V terminologii narazíte pro tyto účely pojem datové proudy. 10.1 Otevření a zavření souboru Chceme-li pracovat se souborem, musíme jej nejprve načíst, resp. otevřít. Slouží k tomu funkce fopen (file open), v níž specifikujeme cestu k souboru a mód přístupu r = čtení, w = zápis, w+ = zápis a přepsání. Při otevírání souborů je dostupných 6 režimů přístupu k souboru: Tabulka 10.1: Režimy přístupu k souboru v C++ r w a pouze čtení, zápis skončí chybou a soubor musí existovat vytvoří se prázdný soubor pro zápis, pokud již existuje, bude obsah smazán otevře soubor pro zápis na konec, soubor se vytvoří, pokud neexistuje r+ soubor se otevře pro čtení i zápis a musí existovat, jinak se vrátí chyba w+ bude vytvořen prázdný soubor pro čtení i zápis, pokud již existuje, obsah se vymaže a+ soubor se otevře pro čtení kdekoli a zápis na konec, vytvoří se, pokud neexistuje Příklad otevření souboru v C++ je txtsoubor = fopen("c:\\opory\\mujsoubor.txt","w"); A soubor bude otevřen, resp. načten do paměti v režimu zápisu. Ještě poznámka v unixových systémech se nerozlišuje tzv. textový a binární přístup k souboru, v jiných operačních systémech ano. Proto pokud bychom uvedli namísto parametru w parametr wb, soubor by byl načten v binárním přístupu. Pokud přístup neuvedeme, automaticky je výchozí textový režim.
79 PRÁCE SE SOUBORY V C++ Při otevírání souboru lze ošetřit, zda v případě neexistujícího souboru bude vypsána chybová hláška. if ((soubor = fopen("textak.txt","r")) == NULL) { printf("tento soubor nemuzu otevrit! Pravdepodobne neexistuje."); exit(1); Ve fragmentu kódu vidíme použití NULL, nulového ukazatele, což v tomto případě znamená, že tento soubor neexistuje. V podmínce se testuje, zda daný soubor existuje. Poznámka k parametru path funkce fopen. Zapíšeme-li pouze název, funkce se pokouší otevřít soubor v aktuální složce, nikoli v té, v níž je uložen program. Samozřejmě můžeme zapsat plnou cestu k souboru, tedy např. C:\priklady\zapisky.txt. Nutno použít zpětná lomítka (backslash) při zadání cesty. Funkce fopen má tedy dva parametry path a mode (cestu k souboru a režim jeho otevření). Jakmile ukončíme práci se souborem, je nutné jej uzavřít. Pokud bychom jej nezavřeli, zbytečně bychom spotřebovali prostředky operačního systému a soubor by nešel otevřít v jiném programu. Pro zavření použijeme funkci fclose, která bude v tomto případě mít za parametr název proměnné, tedy fclose(txtsoubor); Tím soubor zavřeme a odstraníme z paměti pro další použití. V následujících podkapitolách si ukážeme, co vše lze soubory provádět v C++. 10.2 Práce s daty v souboru čtení/zápis Jak s daty v souboru pracovat? C++ nabízí řadu funkcí sloužících ke čtení a zápisu dat. Zmíníme se o funkci fprintf, dále fread a fwrite. Funkce fread umožňuje přečtení bloku dat a fwrite analogicky zápis bloku dat do souboru. Pro zápis do textového souboru použijeme funkci fprintf, která je velmi podobná funkci printf pro formátovaný výstup. Funkce fprintf obsahuje však navíc parametr stream, tedy cestu k souboru, do něhož chceme zapisovat.
PROGRAMOVÁNÍ V C++ 80 Pomocí funkce fgetc lze zjistit výskyt konkrétního znaku v souboru. Naprogramujte si následující kód (nezapomeňte na direktivy na začátku). int main () { FILE * pfile; int znak; int x = 0; soubor=fopen("hledejznak.txt","r"); // proud otevreme v rezimu cteni if (soubor==null) perror ("Soubor nemuzu nacist!"); // perror je funkci zobrazeni chybove hlasky else { do { znak = fgetc (soubor); if (znak == '@') x++; while (znak!= EOF); fclose (soubor); printf ("V souboru se vyskytuje znak @ v poctu %d.\n",n); return 0; Takto můžeme hledat výskyt určitých znaků v souboru a jejich počet. Zvláštní funkcí vztahující se k souborům je fflush, která vyprázdní vyrovnávací paměť a Tato funkce by se měla volat mezi po sobě jdoucím čtením a zápisem nebo zápisem a čtením do souboru. Pokud nastane chyba, vrátí funkce EOF.
81 PRÁCE SE SOUBORY V C++ 10.3 Kompletní příklad s komentářem Závěrečný příklad kapitoly věnujeme zapsání textu do textového souboru. Využijeme v něm základní probrané funkci pro datové proudy. #include <stdio.h> #include <string.h> #define NAZEV "zapis.txt" // makro pro zadani nazvu souboru pro otevreni int main(void) { FILE *soubor; char text[255]; // bude se otevirat soubor definovany v makru NAZEV soubor = fopen(nazev, "a+"); do { // pro ukonceni stisknout ENTER nebo q fputs("napiste slovo, ktere chcete zapsat do souboru\n" "a stisknete ENTER nebo \"q\" k ukonceni: ", stdout); scanf("%254s", text); if (!strcmp(text, "q")) // podmínka, zda stisk q break; printf("zapisuji >> %s <<\n", text); // informace o zapisu textu fprintf(soubor, ">> %s <<\n", text); // zapis textu do souboru while (1); fclose(soubor); // uzavreni souboru return 0;
PROGRAMOVÁNÍ V C++ 82 Doufáme, že po tomto příkladu máte základní znalosti, jak pracovat s datovými proudy v C++ a externími soubory. Samozřejmě je spousta dalších funkcí, které lze využívat, více viz literatura a další webové zdroje. V mnohých případech je třeba v programu pracovat s externími soubory. Tato kapitola se věnuje problematice práce s textovými soubory v C++. Pro práci se soubory C++ poskytuje řadu funkcí. Kapitola je věnována základům práce s datovými proudy. 1. Jaké jsou přístupové režimy k souborům v C++? 2. K čemu slouží bezparametrická funkce main()? 3. jakým způsobem můžeme využívat funkce z knihoven C++ pomocí direktivy #include? Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5.
Kapitola 11 Rekurze princip, využití Po prostudování kapitoly budete umět: chápat význam a princip rekurze; použít rekurzi pro základní algoritmy; přemýšlet, jak rekurzi využít v dalších úlohách. Klíčová slova: Rekurze, rekurzivní funkce, cykly.
PROGRAMOVÁNÍ V C++ 84 V praktickém životě se s rekurzí setkáváme, aniž si to uvědomujeme. Například, už jste někdy viděli obrázek, na němž je tentýž obrázek a vnořuje se do sebe? Ano, i to je příklad rekurze. V matematice se lze rekurzí setkat například u definice oboru přirozených čísel. Nechť 1 je z N a libovolné číslo n je z N, pak platí, že každé číslo n+1 je rovněž z N. Rekurzivní funkce jsou takové, že volají samy sebe. Pro začátek řekněme to, že rekurze umožňuje v řadě případů nahradit konstrukci cyklů, viz kap. 8. V některých programovacích jazycích, jako je například funkcionální Lisp, dokonce neexistuje přímá konstrukce cyklu for či while. Rekurze je zde základním kamenem pro programování cyklů. V oblasti programování rekurzí chápeme opakované použití programové konstrukce při řešení téže úlohy. Oproti cyklu se u rekurze vyskytuje použití téže konstrukce uvnitř konstrukce samotné. Příklad použití je např. při definování datové struktury neboť struktura obsahuje prvek, který má stejnou podobu jako definovaná struktura. Typickým příkladem k použití rekurze jsou nelineární binární stromy. Hlavním smyslem použití rekurze je nahrazení cyklů rekurzivním voláním funkce. Součástí rekurzivní funkce musí být ukončující podmínka, která definuje, ve kterém kroku rekurze končí. Jinak by rekurze teoreticky mohla trvat nekonečně dlouho. Rekurze je způsob deklarování podprogramu, při němž podprogram ve svém těle volá sám sebe. Jak funguje použití rekurze v kódu C++, ukazuje názorně následující obrázek 20. Obrázek 11.1 Princip rekurzivní funkce v kódu C++ Funkce recurse je deklarována na začátku a následně je volána v různých místech programu. 20 https://www.programiz.com/cpp-programming/recursion
85 PEKURZE PRINCIP, VYUŽITÍ 11.1 Tři příklady využití rekurze Typickým, často uváděným příklad pro rekurzi, je výpočet faktoriálu přirozeného čísla n. Rekurzivní je zde volání součinu. Nechť máme spočítat n!, tudíž v každém kroku je volána tatáž funkce pro násobení n (n-1)(n-2) 1. Jelikož už znáte použití cyklů, můžeme si rovnou uvést příklad výpočet faktoriálu s cyklem for a pomocí rekurze. Nerekurzivní výpočet faktoriálu s cyklem for: int Faktorial(int n) { if (n == 0) return 1; int vysledek = n; for (int i = n - 1; i > 0; i--) // cyklus for s dekrementací { vysledek *= i; return vysledek; V tomto případě je použit cyklus for s dekrementací. Pokud n=0, pak se ihned vrátí hodnota 1, proto se použilo return 1; pro podmínku if (n==0). Výpočet faktoriálu rekurzivně bez cyklu int Faktorial(int n) { if (n == 0) return 1; else
PROGRAMOVÁNÍ V C++ 86 return n * Faktorial(n - 1); Kód je podstatně kratší. Je zde pouze podmínka a rekurzivní volání funkce Faktorial, tedy součinu. Další příklad rekurze si uvedeme na následující úloze: Máme nechat vypsat hlášku Vidite cislo, kde X představuje číslo od 0 do 25. Moc účelné to pravda není, ale ukážeme si na tomto příkladu využití rekurzivního volání téže funkce. Opět uvedeme nerekurzivní i rekurzivní způsob řešení 21. Nerekurzivní řešení pomocí cyklu for: #include <iostream> using namespace std; void vypiscislo(int i) { cout << "Vidite cislo " << i << "\n"; int main() { for(int i=0; i<25; i++) { vypiscislo(i); return 0; Následující obrázek ukazuje cyklus, který lze nahradit rekurzí a výstup programu. 21 http://www.cplusplus.com/articles/d2n36up4/
87 PEKURZE PRINCIP, VYUŽITÍ Obrázek 11.2 Nerekurzivní cyklus for A tentýž program převedeme do rekurzivního řešení bez použití cyklu. #include <iostream> using namespace std; void vypiscislo(int i) { cout << "Vidite cislo vypsane rekurzi " << i << "\n"; i++; if(i<25) { vypiscislo(i); int main() {
PROGRAMOVÁNÍ V C++ 88 int i = 0; vypiscislo(i); // zde je uplatneni rekurze namisto pouziti cyklu return 0; Funkce vypiscislo se tak volá rekurzivně. Třetím příkladem, který lze rekurzivně zapsat, je nalezení největšího společného dělitele (dále NSD) dvou přirozených čísel 22. Nebudeme se příliš zabývat matematickou stránkou, pouze si řekneme, že NSD je takové číslo, kterým lze beze zbytku dělit obě čísla a je největší. Příklad slouží pro procvičení a zamyšlení se nad principem algoritmu a proč je možné jej vykonat rekurzivně. Vstupem jsou dvě přirozená čísla n1 a n2. Program v C++ vypadá takto: #include <iostream> using namespace std; int nsd(int n1, int n2); int main() { int n1, n2; "; cout << "Zadejte dve prirozena cisel oddelena mezerou a stisknete Enter: cin >> n1 >> n2; cout << "NSD cisel " << n1 << " & " << n2 << " je: " << nsd(n1, n2); return 0; int nsd(int n1, int n2) { 22 https://algoritmy.net/article/22879/nejvetsi-spolecny-delitel
89 PEKURZE PRINCIP, VYUŽITÍ if (n2!= 0) // je-li n2 ruzne od 0 return nsd(n2, n1 % n2); // rekurzivni vypocet operace modulo else return n1; Základem je využití rekurzivního výpočtu operace modulo (viz kapitola o operátorech). Podívejte se, jak vypadá dialog s uživatelem a výstup programu: Obrázek 11.2: Rekurzivní algoritmus v C++ pro nalezení NSD Námět na cvičení může být napsání nerekurzivního algoritmu pro NSD. Zkuste se zamyslet, jak by vypadal cyklus. Napovíme, že bychom mohli využít cyklus do-while: do { nsd = n1 % n2; // modulo
PROGRAMOVÁNÍ V C++ 90 n1 = n2; n2 = nsd; while (n2!= 0); // podminka pruchodu cyklu Rovněž zkuste rekurzivní algoritmus ještě modifikovat o vrácení chyby, v případě, že zadané číslo n1 nebo n2 je menší než 0. Rekurze se často uplatňuje v algoritmech s binárními stromy. Samozřejmě ne každý cyklus lze rekurzí nahradit, ale v mnoha úlohách lze rekurzí eliminovat složité zápisy cyklů a nechat volat tutéž funkce rekurzivně do ukončovací podmínky. Rekurze je programovací technika, která má za cíl nahradit cykly. Její princip spočívá v opakovaném volání téže funkce. Mezi typické příklady využití rekurze patří výpočet faktoriálu přirozeného čísla n. Dalším využitím je při práci s binárními vyhledávacími stromy. Rekurze může být zpočátku matoucí, ale často umožní nahradit zápisy složitých cyklů. 1. Vysvětlete jednoduše princip rekurze. 2. Co rekurze v programování nahrazuje? Vysvětlete volání rekurzivní funkce. 3. Uveďte některé základní příklady využití rekurze. Zkuste případně najít i jiné. Literatura k tématu: [1] PROKOP, J. Algoritmy v jazyku C a C++. 2. vyd. Praha, Grada, 2012, 169 s., ISBN: 978-80-247-3929-8. [2] VIRIUS, M. Jazyky C a C++: Kompletní průvodce. 2. vyd. Praha, Grada, 2011, 367 s., ISBN: 978-80-247-3917-5.
Kapitola 12 Makra a funkce s proměnným počtem parametrů Po prostudování kapitoly budete umět: využívat základní makra a porozumět jejich významu; definovat makra direktivou #define a #undef; rozlišit makra bez závorek, se závorkami. Klíčová slova: Makra, #define, makro.