Warning
This page is located in archive.

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 Coliru.

Coliru

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 Coliru FAQ

Ideone

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.

Compiler Explorer (Godbolt)

Compiler Explorer 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

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 cppshell nebo online benchmarker quick-bench

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 KDevelop (které je silně svázané s KDE balíčky), přes CodeBlocks, Eclipse CDT, Netbeans, QT Creator, až po 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 zde. XCode pak můžete stáhnout z AppStore.

Windows

Pokud používáte Windows, velmi silně doporučujeme Visual Studio 2017, specificky v Community edition, která je zdarma a smí se nadále používat i pro komerční účely1). Pozor, již neplatí, že studenti katedry počítačů mají přístup ke MSDNAA licencím pro software od Microsoftu.

Instalační soubory samotné naleznete zde, k používání VS 2017 Community Edition déle než 30 dní potřebujete účet, který si můžete vytvořit zde.

CLion

CLion je komerční2) 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 VS2017 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 3).

#include <string>
#include <iostream>
 
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 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 <iostream>
 
int main(){
    std::cout << "Hello world\n";
}
#include <iostream> 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 <iostream>
 
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 <iostream>
 
void say_hello() {
    std::cout << "Hello ";
}

world.h

void say_world();

world.cpp

#include <iostream>
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 jednou4). 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 <iostream>
 
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 <iostream>
#include <vector>
 
int main() {
    std::vector<int> 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í5), 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 <iostream>
#include <vector>
#include <string>
 
int main() {
    std::vector<std::string> 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 <iostream>
 
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 <iostream>
 
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 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ódu6) ji vyzkouší pro n == 3 a a, b, c <= 1000. Zkuste si ho zkompilovat s optimalizacemi.

#include <iostream>
 
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ď?

1)
Licence obsahuje určitá omezení, v případě potřeby si je zkontrolujte.
2)
pro studenty zdarma
3)
defaultní instalace má chybu v balíčkování, dá se vyřešit
4)
Existuje výjimka, v podobě tzv. “inline” funkcích
5)
UB bude vysvětleno na konci cvičení
courses/b6b36pjc/cviceni/cviceni_1.txt · Last modified: 2017/10/02 22:30 by horenmar