Lekce 5 - Céčko a Linux - Code::Blocks podruhé
V minulé lekci, Céčko a Linux - Code::Blocks, jsme se podívali na nastavení a možnosti Code::Blocks a zkusili jsme programátorskou klasiku - Hello world.
Dnes si zkusíme vytvořit jednoduchý program a naučíme se debuggovat. Na
závěr ještě připojím pár tipů, které by vám mohly zpříjemnit práci
Praktický příklad
Nebudeme nic komplikovat a napíšeme velmi jednoduchý program, co bude sčítat dvě hodnoty a vypíše nám výsledek. Zdrojový kód bude následující:
#include <stdio.h> #include <stdlib.h> int main(void) { int a = 5; int b = 10 int c; printf("Jednoduchá sčítačka\n"); c = a + b; printf("Výsledek je %i", c); return 0; }
Zkusme program přeložit a spustit... Co se stane? V programu máme jednu z nejtypičtějších chyb – chybějící středník za příkazem. Kompilace skončila neúspěšně a v logu najdeme vypsané dvě chyby. První je chybějící středník, druhá nám říká, že proměnná c nebyla deklarovaná. Ve skutečnosti je to ale pouze jedna chyba – chybějící středník za deklarací b způsobil, že neproběhla deklarace c. Řádek s chybou nám Code::Blocks zvýraznil červeným čtverečkem. Pokud klikneme na další chybu (v logu), zvýrazní se ta.

Chybu si opravíme a znovu spustíme kompilaci. Tentokrát již proběhne v pořádku. Když poté spustíme program pomocí Run, výstup bude následující:

Ukázali jsme si syntaktickou chybu. Co ale když jde o chybu sémantickou, tedy chybu v algoritmu? Naši sčítačku si trochu upravíme:
int main() { int a, b, c; printf("Jednoduchá sčítačka\n"); printf("Zadej číslo: "); scanf(" %i ", &a); printf("Zadej další číslo: "); scanf(" %i ", &a); c = a + b; printf("Výsledek je %i", c); return 0; }
Program dělá to stejné s tím rozdílem, že sčítaná čísla získáme od uživatele. Napsali jsme si načítání prvního čísla a protože jsme líní, tak jsme jej pro druhé číslo jen zkopírovali a upravili. Teď to zkusíme přeložit. Vidíme sice, že v logu je warning, ale jako „správné“ programátory nás zajímají pouze chyby. Varování můžeme bez problému ignorovat. Hlavní přeci je, aby to nějak fungovalo. Překlad tedy proběhl v pořádku a my můžeme náš upravený program vyzkoušet.

Naše první reakce bude asi ve stylu „Co to je?!?“. Dostali jsme úplně nesmyslný výsledek a přitom ten kód vypadá na pohled dobře... Uznávám, že v našem případě se dá chyba snadno najít, ale představme si program násobně složitější. To již tak jednoduché není. Když tedy narazíme na takovou chybu, jako první se podíváme do logu, jestli se nám překladač náhodou nesnaží něco sdělit.
16 - warning: ‘b’ is used uninitialized in this function [-Wuninitialized]
Na řádku 16 používáme neinicializovanou proměnnou b. Ale jak to? Podíváme se na zmíněný řádek. Najdeme v něm příkaz c = a + b. Z warningu zjistíme, že proměnnou b jsme ještě v programu nepoužili. Ale vždyť do ní přece načítáme druhé číslo, ne? Aha...
Debugging
Pokud si ulehčujete programování kopírováním již napsaného kódu a jeho úpravou, jistě se vám podobná chyba již stala. V malém programu to snadno najdeme. Tu nám navíc pomohou zapnuté direktivy -Wall a -Wextra. Díky nim jsme byli upozorněni na použití neinicializované proměnné. Ale co když bude program složitější? Nebo co když budeme proměnnou b někde v předchozí části programu používat? To už bychom varování nedostali... Co v takovém případě dělat? Jednou možností je strávit hodiny studováním kódu a hledáním chyby. Přesně to dělá mnoho začínajících programátorů, ale je to dobré pouze k získání pocitu frustrace. Existuje další, mnohem lepší možnost – debugger (ladící program, "odvšivovač").
Náš výpočet nám vrací něco neočekávaného. Nastavíme tedy na řádek s výpočtem tzv. breakpoint. Jednoduše řečeno, breakpoint je místo, ve kterém se při debuggování zastaví provádění programu. Breakpoint můžeme jednoduše přidat kliknutím na řádek (hned vedle čísla řádku) nebo pravým kliknutím a vybráním "Toggle breakpoint". Teď spustíme program v debug módu (červená šipka Debug/Continue nebo F8). Zobrazí se nám něco takového:

V okně watches vidíme naše proměnné a co obsahují (hodnoty jsou při spuštění programu náhodné – typicky v nich je hodnota, kterou tam nechala aplikace, které byla paměť předtím přiřazena). Zadáme tedy hodnoty jako normálně. Program normálně běží (nechá si zadat dvě čísla) a zastaví se na řádku s breakpointem (před jeho vykonáním).

Vidíme, že v a máme 10 (i když by tam měla být hodnota 5), v b něco náhodného (mělo by tam být 10) a v c taky nějaký nesmysl (to je v pořádku – do c jsme ještě nic nezapisovali). Aby se provedl následující příkaz (c = a + b), klikneme na Next line nebo stiskneme F7. Provedl se výpočet a vidíme jasně, že správně – zde tedy chyba není. Z hodnot vstupních proměnných ale vidíme, že při jejich načítání není něco v pořádku. Debugger ukončíme (červený křížek nebo Shift+F8) a přidáme breakpointy na řádky, kde načítáme obě proměnné.

Po spuštění programu si všimneme, že se zastavil již před zadáním první proměnné. Pomocí F7 budeme tedy krokovat a sledovat, jak se nám mění hodnoty proměnných. Jen poznámka – F7 musíme stisknout, když máme označené okno Code::Blocks, ne když jsme v XTerm.

Zadali jsme 5 a do proměnné a se uložila 5. Tento příkaz je tedy v pořádku. Všimněme si malé žluté šipečky vedle následujícího řádku – tato nám ukazuje, který řádek se po stisknutí F7 vykoná jako další.

Proběhlo zadání druhé proměnné. Zadali jsme 10, v b by tedy mělo být 10. Místo toho se ale hodnota 10 objevila v proměnné a. Již tedy víme, kde je problém a chybu můžeme opravit.

Zde je výsledný, funkční kód:
#include <stdio.h> #include <stdlib.h> int main() { int a, b, c; printf("Jednoduchá sčítačka\n"); printf("Zadej číslo: "); scanf(" %i ", &a); printf("Zadej další číslo: "); scanf(" %i ", &b); c = a + b; printf("Výsledek je %i", c); return 0; }
Možná vám to teď připadá zbytečné a složité. Ale ve větších a komplikovanějších aplikací se obyčejně programátor bez debuggování neobejde. Toto byl pouze takový úvod a základ a někdy příště se na tuto problematiku podíváme hlouběji.
Pár tipů na závěr
Ještě zmíním pár užitečných maličkostí, co by vám mohly usnadnit
život
Tip 1 - include soubory
Přemýšleli jste někdy, jak je napsaná nějaká knihovní funkce, ale
nevěděli, kde se podívat? Nebo máte nějaký větší projekt, který má
spoustu hlavičkových souborů, ale není realizovaný v Code::Blocks (a nechce
se vám ho vytvářet)? Jednoduché řešení je kliknout pravým tlačítkem na
řádek s #include a "Open #include file"

Tip 2 - záložky
Pokud editujete delší kód, mohou se vám hodit záložky - jednoduše označíte řádek (Ctrl+B nebo pravý klik Bookmarks->Toggle bookmark). Mezi více záložkami se pak můžete pohybovat pomocí Alt+PgUp nebo PgDn.
Tip 3 - Poznámky a TODO
Komentovat, komentovat a komentovat. To je základ. Pokud nevěříte,
uvěříte, až budete pracovat na větším projektů a komentáře nebudou
nebo jich bude málo...
U dlouhých souborů s množstvím komentářů se ale můžeme setkat s tím, že to není příliš přehledné. Určitého zpřehlednění můžeme dosáhnout například pomocí Todo listu (seznam úkolů). Novou položku můžeme přidat například pravým kliknutím na řádek a "Add todo item". Zobrazí se nám následující okno:

Zde můžeme napsat samotný text úkolu, přidat uživatele, který
poznámku přidal (nebo pro kterého je určena - záleží na domluvě),
nastavit prioritu a případně i změnit typ položky a její zápis. Na
výběr jsou TODO, FIXME a NOTE. Názvy myslím mluví za sebe..
Dále máme na výběr několik druhů komentáře (různé nástroje pracují s různým zápisem). Můžeme si také nechat připsat dnešní datum. Také nám nic nebrání napsat položku přímo a vyhnout se tak celému klikání.
// TODO (david#1#3.3.2015): Úplně všechno
Seznam všech takovýchto položek si můžeme zobrazit kliknutím na View->Todo list. Objeví se nám okno, které si můžeme někam připnout a kde můžeme různě filtrovat naše poznámky - jestli se mají zobrazit jen ty v aktuálně vybraném souboru, projektu nebo ve všech otevřených souborech. Dále můžeme vyfiltrovat uživatele a typy. Jak můžeme vidět, položky se nám řadí podle priority (9 je nejvyšší). Dvojklikem na položku se přesuneme přímo na místo, kde se nachází.

Užitečné, ne? Ještě bych zmínil, že můžeme také přidat poznámku přímo k projektu - Project->Notes.
Tip 4 - "zakomentování" kódu
C nepovoluje vnořené komentáře, takže k zakomentování "otravného" kousku kódu, například když debuggujeme, nemůžeme použít blokový komentář. Jedno řešení je použít podmíněný překlad #if, ale to může jít na úkor přehlednosti.
Code::Blocks nám tohle velmi usnadňuje - stačí označit blok a stisknout Ctrl+Alt+C (respektive X pro odkomentování).
Drobnosti na závěr
Kliknutím na proměnnou a stisknutím Alt+N vyvoláte okno, kde můžete snadno přejmenovat všechny její výskyty.
Když jste vedle závorky a stisknete Ctrl+Shift+B, skočíte na druhou závorku, která k ní patří. Pomocí Ctrl+, můžete přepínat otevřené soubory. Ctrl+Enter posune kurzor na konec řádku.
Tak, to bude pro dnešek asi vše
Příští lekce, Céčko a Linux - Filtry, bude o filtrech - aplikacích, jejichž jediný vstup od uživatele je při jejich spuštění a dále již běží autonomně.