{{page>courses:b6b36pjc:styles#common&noheader&nofooter}} {{page>courses:b6b36pjc:styles#cviceni&noheader&nofooter}} ===== Příprava prostředí ===== Účelem prvního cvičení je získat funkční prostředí pro kompilaci C%%++%%14. U Linuxu je třeba rozlišovat mezi kompilátorem (vezme kód, vyplivne spustitelný program) a IDE (usnadňuje psaní kódu), ale Windows i OS X mají silně spjaté kompilátory s IDE. ==== Internetové kompilátory ==== Ať už čekáte, než se vám nainstaluje prostředí (ani XCode, ani VS s instalací zrovna nespěchají), nebo byste si chtěli vyzkoušet kompilátor, ke kterému aktuálně nemáte přístup, internetové kompilátory umožňují rychle vyzkoušet jednoduché programy. Existuje jich více, ale nejčastěji používáme [[http://coliru.stacked-crooked.com/|Coliru]]. === Coliru === [[http://coliru.stacked-crooked.com/|Náš nejoblíbenější online kompilátor]], i když není zrovna nejintuitivnější. Kompilace více souborů je... zajímavá a způsob zadávání vstupu na stdin též. Základní ovládání je jednoduché, jak zkompilovat více souborů nebo jak zadat vstup najdete v [[https://docs.google.com/document/d/18md3rLdgD9f5Wro3i7YYopJBFb_6MPCO8-0ihtxHoyM/edit|Coliru FAQ]] === Ideone === [[https://ideone.com/|Ideone]] umožňuje snadno zadat vstup programu a podporuje více jazyků než jen C%%++%%. Aktuální kompilátory tam ale najdete se zpožděním a nelze u něj měnit kompilační parametry. === Godbolt === [[http://gcc.godbolt.org/|Godbolt gcc]] je online kompilátor, ve kterém sice nemůžete kód spouštět, ale můžete se podívat, do čeho se zkompiluje. Snadný způsob, jak se podívat, co kompilátor opravdu při optimalizaci provede. === Visual C++ Compiler Online === [[http://webcompiler.cloudapp.net/|I Visual C++ má svůj kompilátor online]]. Bohužel, kvalita implementace se zdá být nízká, většina našich pokusů skončila s ''Maximum execution time exceeded!''. Snad časem. === Další === Uvedený výčet samozřejmě není kompletní, chybí v něm například [[http://cpp.sh/|cppshell]]. ==== Vlastní stroje ==== Většina lidí nejradši pracuje na svém vlastním počítači. Připravili jsme pro vás krátký návod, co a odkud nainstalovat na jaké platformě. === Linux === Pro linux doporučujeme Clang jako váš hlavní kompilátor. Nicméně protože na Linuxu Clang používá libstdc%%++%%, což je standardní knihovna GCC, potřebujete primárně aktuální verzi GCC. Cílové verze jsou GCC 5.2+, Clang 3.6+. == IDE == Existuje mnoho různých IDE, která běží pod Linuxem, počínaje [[https://www.kdevelop.org/|KDevelop]] (které je silně svázané s KDE balíčky), přes [[http://www.codeblocks.org/|CodeBlocks]], [[https://www.qt.io/ide/|Eclipse CDT]], [[https://netbeans.org/|Netbeans]], [[https://www.qt.io/ide/|QT Creator]], až po [[https://www.jetbrains.com/clion/|CLion]]. === OS X === Pokud používáte OS X, k získání kvalitního kompilátoru vám "stačí" nainstalovat XCode. XCode vyžaduje ke stáhnutí Apple ID a má 4.5GB, takže se připravte, že instalace chvíli zabere. Pokud ho ještě nemáte, Apple ID si můžete vytvořit [[https://appleid.apple.com/cz/en/#!&page=create|zde]]. XCode pak můžete stáhnout z AppStore nebo [[https://developer.apple.com/services-account/download?path=/Developer_Tools/Xcode_7.2.1/Xcode_7.2.1.dmg|zde ve verzi 7.2.1]]. === Windows === Pokud používáte Windows, velmi silně doporučujeme Visual Studio 2015, specificky v Community edition, která je zdarma a smí se nadále používat i pro komerční účely((Licence obsahuje určitá omezení, v případě potřeby si je zkontrolujte.)). Pozor, již neplatí, že studenti katedry počítačů mají přístup ke MSDNAA licencím pro software od Microsoftu. Instalační soubory samotné naleznete [[https://www.visualstudio.com/cs-cz/downloads/|zde]], k používání VS 2015 Community Edition déle než 30 dní potřebujete účet, který si můžete vytvořit [[http://signup.live.com/|zde]]. == CLion == [[https://www.jetbrains.com/clion/|CLion]] je komerční((pro studenty zdarma)) multiplatformní IDE od JetBrains pro C%%++%%. Pokud používáte jiné JetBrains produkty, jeho ovládání a vzhled vám budou dobře známé, ale osobně preferuji VS2015 kvůli kvalitnějšímu napovídání při používání typové inference a C%%++%%14. == MinGW == MinGW rozhodně nedoporučujeme. Zatímco kompilátor funguje a core language má dobrou podporu, standardní knihovny v sobě mají zásadní díry. Historicky Regex a Random nefungovaly (Random má stále problémy) a například tento triviální program se pod mingw nezkompiluje kvůli chybějícím knihovnám((defaultní instalace má chybu v balíčkování, dá se vyřešit)). #include #include int main(){ int i = 12412; auto s = std::to_string(i); std::cout << "Cool number: " << s << std::endl; } == Cygwin == Pokud chcete, můžete používat Cygwin. GCC pod Cygwinem netrpí problémy MinGW toolchainu, ale předem říkáme, že jsme s ním úlohy netestovali, takže z naší strany nedáváme žádné záruky. ==== Školní stroje ==== V místnostech používaných pro cvičení je při bootu k dispozici ''Ubuntu'', kde najdete Clang ve verzi 3.8, g%%++%% ve verzi 5.3.0, Netbeans, Eclipse a samozřejmě základní linuxí utility. Jako IDE doporučujeme Netbeans, kde si budete muset aktivovat C/C%%++%% plugin, jako čistý textový editor můžete použít třeba Gedit (nebo všudypřítomné vi/nano). //Pokud budete používat Netbeans, nezapomeňte si nastavit nástroje na Clang a standard jazyka na C%%++%%14// Před prvním přihlášením si budete [[https://www.felk.cvut.cz|muset nastavit heslo]]. ===== Ukázky ===== ==== Vstup a Výstup ==== Základní výstup (tisk do konzole) funguje v C%%++%% přes tzv. "streamy" (proudy). Konzolový výstup (stdout) je reprezentován objektem ''std::cout'', vstup (stdin) je pak reprezentován objektem ''std%%::%%cin''. === Hello World === Tato ukázka reprezentuje minimální program "Hello world". Po jeho vykonání bude na konzoli napsáno //Hello World//. #include int main(){ std::cout << "Hello world\n"; } ''#include '' umožní programu používat třídy pro standardní vstup. Operátor '<<' slouží k zápisu do proudu. === Jednoduchý vstup === Tento program ukazuje načítání vstupu. #include int main(){ std::cout << "Dej mi cislo\n"; int num; std::cin >> num; std::cout << "Cislo bylo: " << num << '\n'; } %%>>%% je operátor čtení z proudu. std::cin >> num; Přečte ze standardního vstupu jedno číslo a uloží jeho hodnotu do num. Následně jeho hodnotu vypíše na výstup (spolu s trochou textu). Všimněte si, že se operátor výstupu dá řetězit. Stejným způsobem se dá řetězit i čtení: int num1, num2; std::cin >> num1 >> num2 Tyto dva řádky zadeklarují dvě čísla (proměnné typu int) a následně načtou dvě čísla ze stdin a uloží je do zadeklarovaných proměnných. ==== Kompilace více souborů ==== Jakmile začneme mít více kódu, je dobré ho rozdělit do více souborů. "Hello world" sice není zrovna složitý program, ale jako ukázka nám poslouží. Mějme 5 souborů: ''main.cpp, hello.h, hello.cpp, world.h, world.cpp'' s obsahem: //main.cpp// #include "hello.h" #include "world.h" int main(){ say_hello(); say_world(); } //hello.h// void say_hello(); //hello.cpp// #include void say_hello() { std::cout << "Hello "; } //world.h// void say_world(); //world.cpp// #include void say_world(){ std::cout << "world\n"; } Souborům s příponou .h říkame hlavičkové. Tyto soubory slouží k deklaraci funkcí. V souborech s příponou .cpp pak jsou funkce implementované. O přesných rozdílech mezi deklarací a implementací (nebo též definicí) si povíme později. Pro teď jenom řekneme, že deklarace vypadá jako začátek definice, ale bez těla (bez části, která je mezi složenými závorkami ''{'' a ''}'') a ukončena středníkem. Deklarace je způsob jak říci, že určitá funkce existuje a smí se používat, i když je součástí jiného souboru. Alternativně se na ni dá nahlížet jako na způsob, jak slíbit, že nějaká funkce bude "časem" existovat. Definice pak je kompletní funkce a v programu může být každá funkce definována pouze jednou((Existuje výjimka, v podobě tzv. "inline" funkcích)). Deklarací může program obsahovat libovolné množství. ==== Kousek standardní knihovny ==== Nebudeme zatím řešit standardní knihovnu nijak do hloubky, ale krom standardního vstupu a výstupu se ještě podíváme na dva kousky standardní knihovnu. Třídu pro řetězce, ''std::string'', která zjednodušuje práci s řetězci, a ''std::vector'', což je třída podobná javovské třídě ''ArrayList''. === std::string === ''std::string'' se dá načítat přes ''std::cin'' a vypisovat přes ''std::cout''. Pozor, načítání funguje po "slovech", tj. načtou se všechny znaky mezi dvěma prázdnými místy (mezery, tabulátory, konce řádků...). Co to znamená? Mějme tento kousek kódu: #include int main() { std::cout << "Napis sve jmeno:\n"; std::string jmeno; std::cin >> jmeno; std::cout << "Ahoj " << jmeno << '\n'; } Pokud mu jako vstup dáme ''Jan'', pak výstup bude Napis sve jmeno: Jan Ahoj Jan Pokud mu ale jako vstup dáme celé jméno, třeba ''Jan Novak'', pak výstup bude stále pouze samotné jméno. Napis sve jmeno: Jan Novak Ahoj Jan === std::vector === ''std::vector'' je rostoucí pole. Není potřeba předem určit velikost, stačí vytvořit vektor a přidávat/odebírat prvky dle libosti. Jedná se o šablonovou třídu, což znamená, že je potřeba deklarovat, jaký typ chceme do vektoru ukládat. Do vektoru můžeme ukládat primitivní i uživatelem definované typy. #include #include int main() { std::vector numbers; std::cout << "Napis 5 cisel.\n"; for (int i = 0; i < 5; ++i){ int temp; std::cin >> temp; numbers.push_back(temp); } std::cout << "Cisla byla:\n"; for (int i = 0; i < 5; ++i){ std::cout << numbers[i] << '\n'; } } === Iterace === Předchozí ukázka nejdříve načetla 5 čísel a pak vypsala prvních 5 prvků z vektoru. Co by se ale stalo, pokud bychom změnili načítací smyčku, aby načítala pouze 4 prvky? Není to jisté, protože to je tzv. nedefinované chování((UB bude vysvětleno na konci cvičení)), ale důležité je, že bychom smyčky, kde chceme přejít přes celý kontejner, měli psát trochu jinak. #include #include #include int main() { std::vector vec; std::cout << "Zadej 5 jmen.\n"; for (int i = 0; i < 5; ++i){ std::string temp; std::cin >> temp; vec.push_back(temp); } std::cout << "Jmena byla:\n"; for (std::string s : vec){ std::cout << s << '\n'; } } Někteří z vás určitě poznávají tzv. "for-each" smyčku, stejnou jako v Javě. Určitě si též umíte představit, že pokud by daný vector byl šablonován na složitém typu, nebylo by pohodlné jej použít. Naštěstí existuje i mnohem jednodušší forma: ... std::cout << "Jmena byla:\n"; for (auto s : vec) { std::cout << s << '\n'; } ''auto'' jako typ říká kompilátoru, ať zjistí typ "na pravé straně" výrazu a doplní ho. Více o použití ''auto'' v deklaracích proměnných si povíme později. ==== Koutek nedefinovaného chování ==== Pojem nedefinovaného chování (UB -- Undefined Behavior) v C%%++%% vyjadřuje situaci, ke které dle standardu jazyka "nemůže dojít". Pozor, to neznamená, že jazyk brání tomu, aby k nim došlo, ale kompilátor při optimalizacích může nedefinované chování zanedbat a předpokládat, že si programátor "nějak" zajistil, aby k němu nedošlo. Co to znamená v praxi, si ukážeme na několika příkladech. === Příklad 1 === Zkuste si zkompilovat a spustit tento program, nejdříve bez optimalizací: #include int main(){ int i = 1; while (i > 0){ std::cout << "Ahoj\n"; i *= 2; } } Měl by vám vytisknout 31 řádků s "Ahoj". Nyní si ho zkuste zkompilovat s optimalizacemi (''-O3'' pro GCC, ''Release'' konfigurace ve Visual Studiu a XCode). Co vám vytiskne teď? === Příklad 2 === Zkuste si zkompilovat a spustit tento program. Nezapomeňte zapnout C%%++%%14. #include int test(int x) { if (x > 0) { return 1; } x -= 1'000'000'000; if (x > 0) { return 2; } return 1; } int main(){ std::cout << test(1) << std::endl; std::cout << test(-2'000'000'000) << std::endl; } Co je na výstupu, pokud spustíte program zkompilovaný s optimalizacemi? A co pokud spustíte program zkompilovaný bez nich? === Příklad 3 === Možná jste slyšeli o [[https://en.wikipedia.org/wiki/Fermat%27s_Last_Theorem|Velké Fermatově větě]]. Dlouho se nevědělo, jestli je opravdu pravdivá, nebo ne, ale v roce 1995 se ji po dlouhé době podařilo prokázat. Představte si, že tomu důkazu nevěřite a chcete si ji ověřit. Tento kousek kódu((Přejato z [[http://blog.regehr.org/archives/161|]])) ji vyzkouší pro ''n == 3'' a ''a, b, c %%<=%% 1000.'' Zkuste si ho zkompilovat s optimalizacemi. #include bool fermat() { const int MAX = 1000; int a = 1, b = 1, c = 1; while (true) { if ((a*a*a) == ((b*b*b) + (c*c*c))) { return true; } a++; if (a > MAX) { a = 1; b++; } if (b > MAX) { b = 1; c++; } if (c > MAX) { c = 1; } } return false; } int main() { if (fermat()) { std::cout << "Fermat's Last Theorem has been disproved.\n"; } else { std::cout << "Fermat's Last Theorem has not been disproved.\n"; } } Pokud vám program vypsal, že Velká Fermatova věta byla vyvrácena, nechte si vypsat hodnoty //a, b, c//, pro které je vyvrácena. Co vám program vypsal teď? Video Tutorial: [[https://www.youtube.com/watch?v=Cq1h1KPoGBU]]