Cvičení 1: Úvodní cvičení

Úkol 1: Vytvoření Git repozitáře pro odevzdávání domácích úkolů

Pro odevzdávání domácích úkolů je potřeba založit Git repozitář ve fakultním GitLabu. Ve zkratce lze říct, že Git je něco jako zálohovací nástroj, který je vhodný pro programátory (obecně pro práci s čistým textem), více se dozvíte třeba tady, tady a nakonec třeba i tady.

V oficiálních pokynech pro GitLab naleznete tento postup i s obrázky.
Pokud máte repozitář PB071 z předchozích let, upozorněte cvičící dříve, než začnete něco dělat.
  1. Přihlaste se do fakultního GitLabu.

  2. V levém horním rohu zadejte do vyhledávacího pole Student Repository Template a pak klikněte na projekt PB071 Low-level programming / Student Repository Template (nebo klikněte na tento odkaz).

  3. Pak najděte tlačítko Fork a klikněte na něj.

  4. Název projektu změňte na PB071 a cestu pb071.

  5. Viditelnost projektu nastavte na Private a klikněte Fork.

  6. Pokud vidíte nastavení Branches to include, vyberte možnost Only the default branch main.

  7. Přejděte na úvodní stránku projektu.

  8. V levém panelu s možnostmi klikněte na SettingsGeneral a najděte kategorii Advanced, kde klikněte na Expand. Zde klikněte na tlačítko Remove fork relationship.

  9. Nakonec přidejte do projektu uživatele pro Kontr. V levém panelu s možnostmi klikněte na Members (poslední položka v nabídce Project information). Na kartě Invite member vyberte uživatele kontr s právem Reporter. Stejným způsobem přidejte i svoje cvičící, taky s právem Reporter.

Nejpozději při spuštění testů HW01 vám budou přiřazeni i opravující. Ty pak stejným způsobem přidáte do projektu, sice s právem Developer.

Úkol 2: Připojení na fakultní počítače

Teď je potřeba, abyste si vyzkoušeli pracovat na fakultním serveru, skrz který se odevzdávají domácí úkoly. Fakultní server je ale vhodný i na testování domácích úkolů před odevzdáním — server se může chovat trochu jinak než váš lapťop, což by mohlo způsobit, že vám neprojdou některé testy. A to nechcete.

Na serveru aisa běží operační systém Linux, ale není třeba se toho bát, na takové „domácí žvýkání programování“ vám plně postačí pár příkazů, které si teď vyzkoušíte.

První fáze: Připojení

Pokud používáte OS Windows, potřebujete spustit program Putty. Na školních počítačích je již k dispozici, program lze stáhnout třeba tady. Je třeba zadat adresu aisa.fi.muni.cz (pokud budete ve fakultní síti — wlan_fi, eduroam — , pak stačí jenom aisa) a připojit se. Terminálové okno se ještě zeptá na vaše přihlašovací údaje, které vyplníte. Nelekněte se, že se při zadávání hesla nezobrazují žádné znaky — je to bezpečnostní opatření.

Pokud jste na Linuxu, pak postačí v terminálu (terminál naleznete ve „start“ nabídce) spustit:

ssh aisa

Pokud vám přijde, že je otrava při každém přihlášení zadávat jméno a heslo, když to „přece jste vy“, pak vězte, že to jde nastavit tak, abyste nemuseli nic zadávat.

Druhá fáze: Základní příkazy

Nejprve zjistěte, co máte ve svém domovském adresáři. To uděláte příkazem ls.

Teď si nastavte v Gitu svoje jméno a e-mail, které se zobrazují s commity:

git config --global user.name "Jméno Příjmení"
git config --global user.email "‹xlogin›@fi.muni.cz"

Jméno, Příjmení a ‹xlogin› nahraďte skutečnými hodnotami.

Následně si stáhněte svůj repozitář z GitLabu:

git clone https://‹xlogin›@gitlab.fi.muni.cz/‹xlogin›/pb071.git

Po zadání hesla se vám stáhne (zatím) prázdný repozitář do nově vytvořeného adresáře pb071. Do adresáře vstoupíte pomocí příkazu cd pb071. Teď ale můžete využít skvělé vychytávky terminálu — klávesy TAB. Stačí, když zadáte cd p a stisknete tabulátor. Pokud existuje pouze jeden adresář se jménem začínajícím na p, terminál doplní zbytek názvu. V opačném případě dalším stiskem TAB zobrazíte všechny takové adresáře a postupným prolínáním dopisování názvu a stisků klávesy TAB se dostanete ke kýženému výsledku.

  1. Dobrou praktikou při zakládání Git repozitáře je ihned začít prvním commitem (verzí). Někdy se pro tento účel vytváří soubor README s popisem projektu, který se však pak často mění. Čistší možnost je vytvořit první verzi prázdnou. Díky tomu se lze v historii vrátit až k úplnému začátku projektu, kdy byl prázdný.

    Repozitář, který jste si před chvíli naklonovali, již iniciální commit má. Můžete si ověřit, že je prázdný, příkazem

    git log --stat

    kde uvidíte, že Initial commit nezavádí do projektu žádné změny. Pokud byste potřebovali takový commit vytvořit (teď to dělat nemusíte), dělá to příkaz

    git commit --allow-empty -m 'Initial commit'

    Git normálně prázdný commit vytvořit nedovolí, protože v jiných situacích nedává smysl. Přepínač --allow-empty se proto používá pouze při zakládání projektu. Za přepínačem -m pak následuje zpráva, která popisuje novou verzi. První verze projektu se typicky volá Initial commit.

  2. Další důležitou částí repozitáře je soubor .gitignore, více se o něm se dozvíte na konci této stránky nebo v manuálu.

    Pokud jste postupovali návodem na začátku stránky, pak je již součástí repozitáře a můžete si vypsat jeho obsah:

    less .gitignore
  3. Možná jste si všimli, že v repozitáři je i adresář etc. Tento repozitáře obvykle nemají. Nám slouží pouze pro uložení souborů, které budeme potřebovat zřídka a za zvláštních okolností.

    Jednou z nich je nastavení repozitáře po klonování. I když se Git snaží zabránit rozbití repozitáře, pravidlům předmětu nerozumí a nebude vám bránit například stáhnout seminář do větve s úkolem. Proto jsme pro vás připravili pomůcku, která vám zabrání udělat některé z těchto chyb. Tato pomůcka funguje jako háčky (angl. hooks), která při různých operacích s Gitem ověří, že neděláte něco zjevně špatně.

    Háčky nastavte spuštěním následujícího skriptu:

    etc/install-hooks.sh

    A je to! Pokud jednou háčky nastavíte, příště to již dělat nemusíte. Tento příkaz je třeba opakovat pouze po naklonování repozitáře po příkazu git clone, například pokud si jej stáhnete na další počítač.

  4. V další části vyzkoušíme odevzdání testovací úlohy hello. Ostré domácí úlohy budou mít kostru zveřejněnou v původním repozitáři. Pro tuto testovací úlohu si musíme vystačit sami.

  5. Nejdříve vytvoříme novou větev se stejným názvem, jaký má kód úkolu:

    git switch -c hello
  6. Zda příkaz fungoval a jsme ve správné větvi ověříme příkazem

    git status
  7. V adresáři pb071 si vytvořte adresář hello pomocí příkazu mkdir hello a vstupte do něj.

  8. Teď si zkuste vytvořit soubor hello.c, k čemuž využijete příkaz touch hello.c.

  9. Pokud si jeho obsah vypíšete příkazem cat hello.c, zjistíte, že je prázdný.

  10. Nějaký obsah do něho dostanete takto: echo '#include <stdio.h>' > hello.c.

  11. Kontrolou pomocí příkazu cat hello.c si ověříte, že teď už soubor není prázdný.

Pokud chcete soubor editovat, napište příkaz nano hello.c. Spustí se jednoduchý textový terminálový editor. Ovládá se pomocí klávesových příkazů, kdy základní příkazy vidíte ve spodní části okna. ^O znamená Ctrl + O. Existují i sofistikovanější terminálové editory, ale zpočátku se spokojíte s tímto. Do souboru vložte následující obsah:

#include <stdio.h>

int main(void)
{
    printf("Hello World\n");
    return 0;
}

Když máte hotový program, je na čase jej přeložit. To provedete příkazem gcc -std=c99 -o hello hello.c. Tento příkaz je už trochu složitější, ale budete se s ním během semestru setkávat tak často, že vám to pak už ani nepřijde.

Pokud se vám podařilo program přeložit, můžete ho spustit: ./hello (s výhodou se dá použít tabulátor na doplnění jména souboru).

Třetí fáze: Nahrání změn do repozitáře

Každá změna souboru se může v repozitáři nacházet v jednom z následujících stavů:

  • rozpracovaná, nebude v příštím commitu — modified

  • připravená, bude v příštím commitu — staged

  • lokálně uložená — commited

    • uložená i na GitLabu — pushed

Stav se mění pomocí editoru a rozličných Gitových příkazů. Ty nejzákladnější jsou:

  • git status — Zjistíte, v jakém stavu se vám nacházejí soubory, tedy jestli se něco změnilo:

    • vypíše soubory, u kterých jsme zatím nesledovali změny (untracked),

    • soubory, v kterých jsme udělali změny, ale nepatří do následujícího commitu (modified, not staged for commit),

    • soubory, které jsme zařadili do následujícího commitu (staged).

  • git diff — Zjistíte, co přesně se změnilo od posledního commitu — verze repozitáře.

  • git add hello.c — Zařadí změny v souboru hello.c do dalšího commitu — změny budou označeny ako připravené.

  • git commit -m "Add hello" — Uloží všechny připravené změny — vytvoří novou verzi.

  • git push — Uloží veškeré commity z aktuální větve na fakultní server.

Přidejte vámi vytvořený soubor do připravených pomocí git add hello.c. Proveďte commit a push. U push budete muset specifikovat do jaké větvě chcete kód uložit git push --set-upstream origin hello.

Úkol 3: Odevzdání testovacího domácího úkolu

Když se vám podařilo vytvořit, „commitnout“ a „pushnout“ program, je na čase ho zkusit odevzdat. To provedete následujícím příkazem:

/home/kontr/odevzdavam pb071 hello nanecisto

Teď už stačí jenom počkat, až Kontr vyhodnotí vaši úlohu. Režim nanečisto spouští velmi omezenou sadu testů, které mají sloužit pouze pro kontrolu, že neděláte něco velmi špatně. Při velkých domácích úkolech se očekává, že si svoje řešení otestujete před odevzdáním sami.

Pokud jste s výsledky testu nanečisto spokojeni, zkuste ještě režim odevzdání naostro, za který můžete při splnění testu získat 1 bonusový bod:

/home/kontr/odevzdavam pb071 hello naostro

Pro odevzdání úkolu v režimu naostro ale musíte splnit několik podmínek. Jednou z nich je vytvořený Merge Request pro daný úkol. Jak ho vytvořit se podíváme v další části tohoto cvičení.

Úkol 4: Vytvoření Merge Request

Domácí úkoly musí projít kontrolou kvality kódu. Bez této kontroly nebudou žádné body. Proto si vytvoření žádosti vyzkoušíme.

  1. V GitLabu na levém panelu najděte Merge requests a klikněte na New merge request.

  2. Na další obrazovce si zkontrolujte, že Source branch je hello ve vašem repozitáři, a Target branch je main taky ve vašem repozitáři. Pokud zde uvidíte něco jiného, pak jste repozitář nastavili špatně. Nejlépe zavolejte svoje cvičící.

  3. Pokud je všechno v pořádku, klikněte na Compare branches and continue.

  4. Zde pojmenujte MR Draft: Hello. Slovíčko Draft: je zde hrozně důležité. Zabrání nám totiž omylem kliknout na Merge předtím, než odevzdání uvidí opravující.

  5. Jako Assignee nastavte sebe. Při normálních úkolech dále nastavíte Reviewer na svého opravujícího. Protože teď nejspíš ještě nevíte, kdo to bude, nastavte zde taky sebe.

  6. Nakonec klikněte na Create merge request.

Tímto je žádost o kontrolu kvality kódu vytvořena.

Úkol 5: Odevzdání testovacího domácího úkolu znovu a lépe

Když už máte vytvořený Merge Request, tak můžete úkol zkusit odevzdat znovu v režimu naostro:

/home/kontr/odevzdavam pb071 hello naostro
Po doručení úspěšného hodnocení byste měli v poznámkovém bloku v IS MU najít i bodové hodnocení.

Více o příkazu na odevzdávání domácích úloh naleznete zde.

Úkol 6: Formátovaný výpis

Jako poslední úkol tohoto týdne si zkusíme něco naprogramovat. Podstatnou částí všech programů, které budete kdy programovat je předání informací uživateli. Počítač pro svoji práci používá téměř výhradně binární podobu dat, které zvládá rozumět, nicméně pro uživatele nebo programátora jsou data v takovém formátu většinou nesrozumitelná. Proto je potřebné umět různé typy informací transformovat a převádět do člověku srozumitelné formy.

Nejzákladnější formou, se kterou budeme v rámci tohoto předmětu nejčastěji pracovat, je textová reprezentace. Jedná se o formát zápisu dat, který využívá znaky ASCII tabulky. Pro převod interní reprezentace informací na textová data budeme v rámci tohoto předmětu nejčastěji používat funkce z rodiny printf(3) resp. scanf(3), které s pomocí tzv. formátovacího řetězce, dokáží převést požadované proměnné do textové podoby a vypsat je v zadaném formátu.

Více o funkcích s formátovacími řetězci naleznete v dokumentaci C na cppreference.com, příklad je uvedený na konci.

Git se nejlépe naučíte ovládat tréningem. Proto si zkuste v repozitáři držet i kódy ze cvičení — ano, to můžete. Doporučujeme pro to použít větvu main, do které se vrátíte příkazem git switch main (bez -c).

Z odkazu na začátku cvičení si stáhněte kostru a rozbalte ji.

  • Kostru naleznete v repozitáři, který jste na začátku cvičení klonovali. Vizte postup pro stahování cvičení.

  • I když si pro první cvičení vystačíte s kompilací manuálním zpuštěním kompilátoru z terminálu, do dalších cvičení se může hodit návod pro CMake.

V souboru printer.c jsou deklarovány následující proměnné:

int number = 12;
long long long_number = 0x27BFE;
double floating_number = 2.71828182;
char single_char = 'A';
char string[] = "This is string";

Vaším úkolem bude vytvořit formátovací řetězce kterými vypíšete výše uvedené proměnné ve tvaru

Number is: 12
Long number is: 27bfe in hexadecimal
Long number is: 162814 in decimal
Floating number is: 2.71828182
Floating number is: 2.7182 with precision to 4 decimal places
Char is: 'A'
String is: "This is string"

Tyto řetězce vypište do připravených funkcí printf(3) v souboru printer.c

Po dopsání těchto formátovacích značek, můžete svůj kód zkompilovat.

Formátovací značky

Pro vypisování dat pomocí funkcí s formátovacími řetězci (např. printf(3)) budete využívat nejčastěji:

  • %d vypíše číslo typu int v desítkové reprezentaci

  • %x vypíše číslo typu unsigned int v šestnáctkové reprezentaci

  • %lld vypíše číslo typu long long v desítkové reprezentaci

  • %llx vypíše číslo typu unsigned long long v šestnáctkové reprezentaci

  • %f vypíše číslo typu float jako desetinné číslo

  • %lf vypíše číslo typu double jako desetinné číslo

  • %c vypíše jeden znak typu char

  • %s vypíše řetězec typu char * nebo char []

Všem těmto značkám lze ještě specifikovat číselně jejich minimální šířku, která se uvádí před značku.

  • %5d značí, že dané číslo bude vypsáno nejméně na pěti znacích, pokud je výsledná reprezentace kratší, výpis je zleva vyplněn znaky mezery, například:

    • Number: |%5d| vypíše Number: |   25|

    • String: |%10s| vypíše String: |      Ahoj|

    • String: |%3s| vypíše String: |Ahoj|

  • Značkám pro čísla s plovoucí desetinnou čárkou lze navíc specifikovat přesnost na počet vypsaných desetinných míst, například

    • Pi is: |%.2f| vypíše Pi is: |3.14|

    • Pi is: |%7.2f| vypíše Pi is: |   3.14|

    • Pi is: |%2.2f| vypíše Pi is: |3.14|

Pokud potřebujete v řetězci použít znak " můžete využít speciálního znaku \, který ruší řídící charakter znaku za ním, například, řetězec obsahující uvozovku bude vypadat následovně "\"", řetězec obsahující zpětné lomítko potom bude vypadat takto "\\".

Další zdroje a informace

Tato sekce doplňuje cvičení o další zdroje a informace, které vám mohou pomoci při implementaci tohoto cvičení. Zde již žádné úkoly nehledejte.

Další zajímavé Gitové příkazy

  • git restore --staged hello.c — Tímto příkazem přesunete soubor hello.c zpět ze stavu připravený do stavu rozpracovaný.

  • git pull — Zkontrolujete, jestli a jak se liší váš lokální repozitář od toho na serveru, a stáhnete případné změny.

  • git pull --rebase — Vytvořili jste commit, který jste chtěli uložit na server, ale příkaz git push selhal, protože Updates were rejected because the tip of your current branch is behind its remote counterpart.

  • git restore cesta/k/souboru — Udělali jste změny v soubor, které ale chcete vrátit do stavu „jako na začátku“.

  • git reset --soft HEAD~1 — Udělali jste commit, který je špatný, a rádi byste se ho zbavili (změna stavu z uložený na připravený).

  • git log — Zobrazí historii commitů.

  • git stash — Udělali jste změny, které si chcete na moment odložit.

  • git stash pop — Chcete si opět pracovat na odložených změnách.

Další šikovné linuxové příkazy

  • Chcete něco zkopírovat? cp co kam

  • Chcete něco přejmenovat/přesunout? mv co kam

  • Chcete smazat soubor? rm co

  • Chcete smazat složku? rm -rf co (POZOR — opravdu to smaže všechno, používejte s rozmyslem)

  • Chcete se dostat do domovského adresáře? Stačí zadat cd

Všechny příkazy jsou uvedeny v manuálu.

Porovnávání souborů

Linux je velmi dobrý systém pro práci s textovými soubory. Co se vám bude velmi hodit, je textové porovnání dvou souborů. Zkuste si teď udělat malé demo:

  1. Skočte do složky ~/pb071/hello/ pokud v ní ještě nejste.

  2. Vytvořte soubor expected.txt s obsahem Hello World!. Například takto: echo 'Hello World!' > expected.txt

  3. Přesměrujte výstup programu ./hello do souboru _result.txt. To provedete takto: ./hello > result.txt

  4. Zjistěte, jestli se soubory liší, příkazem diff -c expected.txt result.txt Co z toho vyčtete?

  5. Opravte program tak, aby vypisoval to, co je v souboru expected.txt. Co vypíše příkaz diff?

$ diff -c expected.txt result.txt
*** expected.txt 2016-01-14 10:37:11.000000000 +0100
--- result.txt  2016-01-14 10:37:40.000000000 +0100
***************
*** 1 ****
! Hello World!
--- 1 ----
! Hello World`

Co vůbec dělá zobátko?

Jednoduché zobátko doprava způsobí, že se výstup programu zapíše do souboru, místo toho, aby se vypsal na obrazovku. Když zobátko obrátíte, pak spuštěný program dostane obsah souboru na vstup — jako byste to psali na klávesnici vy.

Pokud byste chtěli převést do souboru chybový výstup programu, provede se to takto: program 2> chybovy_vystup.txt

Pokud byste chtěli jenom přidat na konec souboru, použijete dvě zobátka namísto jednoho.

K čemu je magický .gitignore?

Při programování větších aplikací se vám častokrát stane, že při překladu, testování, nebo při práci s IDE vygenerujete soubory, které nejsou nezbytné pro překlad, běh nebo vývoj programu. Případně, jako je tomu u jazyka C, vznikají překladem přesně definované soubory, které jsou vytvářeny pro váš počítač s na něm nainstalovaným OS a nemusejí obecně fungovat. Takové soubory ve většině případů nechceme s ostatními sdílet, ale naopak chceme aby byly při verzování zdrojového kódu ignorovány.

A přesně tohle má na starosti .gitignore — popisuje pravidla pro git, tedy soubory, které nemají být označeny pro commit (budou ignorovány) kdykoliv použijete příkaz git add.

Silně doporučujeme použít ve vašem repozitáři námi dodaný .gitignore, pomůže vám vyhnout se mnoha nepříjemnostem při odevzdávání domácích úkolů.