{{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]]