Tato lekce je jednou z nejdůležitějších - pojednává o tom, jak odstranit chyby (česky se tomuto procesu říká ladění). Pokud jste psali nějaké vlastní programy, víte, že odhalit chybu je občas mnohem těžší, než napsat celý program znova. Pokud vám taková zkušenost chybí, nezoufejte, je to jen otázka času.
Bug = brouk
Debugging = odstraňování brouků z programu. (Jenomže Čechům se do programů
spíše vloudí mouchy :( )
Obsah:
Čím méně chyb budeme dělat, tím lépe. Abychom jejich počet snížili na minimum a usnadnili si jejich případné hledání, je dobré držet se následujících zásad:
Budete-li dodržovat veškeré zde uvedené zásady, vloudí se do vašeho programu chyba jen výjimečně.
Občas se přece jen stane, že se nějaká ta chybička vloudí...
Chyby dělíme na dvě velké skupiny:
Během psaní programu je neustále dobré si ho spouštět a kontrolovat, zda pracuje tak, jak má. Odstraníme tak všechny syntaktické chyby a i některé chyby sémantické. Později pak máme téměř jistotu, že jsme chybu neudělali v té části programu, kterou jsme několikrát zkontrolovali.
K odhalení syntaktické chyby stačí pokusit se program
zkompilovat. K
tomu abychom odhalil chybu sémantickou, nestačí občas ani roky testování.
Všeobecný postup je takovýto - nejprve se pokusíme náš program spustit. Hlásí-li
program syntaktickou chybu, zamyslíme se nad tím, jak ji vhodně opravit,
opravíme ji a zkusíme program spustit znova.
Sémantické chyby:
Je to jednoduché, stačí chybu najít a přepsat. Přepsat chybu bychom již měli umět. Horší je chybu najít.
Nejjednodušší je nalézt chyby syntaktické. Ty totiž ohlásí již překladač. (Zapomenutý end tvoří výjimku, ohlásí se až na samém konci programu.)
K nalezení chyb sémantických je nutno použít drsnějších nástrojů. Zamyslete se nejprve nad tím, co by danou chybu mohlo způsobovat a v které části programu se daná chyba může vyskytovat (často je to značně daleko od jejího prvního projevu). Pozorně si prohlédněte podezřelá místa a zkontrolujte, zda jste na nic nezapomněli.
Že jste chybu nenašli? Máte nyní několik možností:
Ad 1) Zapneme omezující volby a doufáme, že se chyby sémantické stanou pod přísnějším pohledem překladače syntaktickými. (Nejde použít v případě, že vědomě využíváme některých výhod volnější syntaxe.)
Ad 2) Procházíme celý kód od začátku a každý příkaz zdůvodňujeme
- jestli dělá skutečně to, co má.
Ad 3) Celé bloky programu lze vyřadit použitím těchto závorek (* Blok
programu.... *)
Dá se to ale udělat pouze tehdy, pokud jste tyto závorky nepoužívali k psaní
komentářů. (A podobně pokud používáte ke komentování pouze (* a *), můžete
celé úseky programu vyřadit z činnosti pomocí složených závorek.)
Vyřazování celých částí programu se používá několika způsoby -
Ad 4) IDE je k nám milostivo a umožňuje využívat tzv. podmíněný překlad
- části programu, které se přeloží pouze tehdy, je-li splněna daná podmínka
{$DEFINE Jmeno} Oznámí překladači, že jsme definovali symbol
Jmeno
{$UNDEF Jmeno} Oznámí překladači, že jsme zrušili definici
symbolu Jmeno
{$IFDEF Jmeno} Následující příkazy až k {$ENDIF} se přeloží pouze v případě, že je definován symbol Jmeno
{$IFNDEF Jmeno} Následující příkazy se provedou pouze v případě,
že Jmeno není definován.
{$IFOPT Switch} Následující příkazy se vykonají pouze tehdy, když
má daný přepínač danou hodnotu {$IFOPT X+} uses Strings {$ENDIF}
{$ELSE} Následující příkazy se vykonají, nebyla-li splněna předchozí
podmínka (IFDEF,IFNDEF, IFOPT)
Nejčastěji se podmíněného překladu používá tak, že definujeme
konstantu DEBUG a v jednotlivých procedurách si podmíněně necháme vypsat
obsah všech důležitých proměnných (a vůbec, občas je dobré i ohlásit,
v jakém stádiu se výpočet nachází)...
Poté konstantu DEBUG zrušíme a ve výsledném .EXE není nic poznat.
Ad 5) Krokování je velice užitečné, dělí se na několik různých úrovní.
Občas se nám hodí zkratky Ctrl-F2 (resetuje program, takže ten začne pak
znovu od začátku)
F4 - Provede program a zastaví se na řádce, na které je kurzor. Od této
pozice lze pak krokovat
Také můžeme využívat možností nabídky Debug - Watch. Ta nám umožní
sledovat hodnoty zadaných proměnných, Evaluate/Modify - Ctrl-F4 nám dává
možnost zobrazené hodnoty i měnit.
Output -zobrazí co se v daném momentě nachází na obrazovce programu.
Nepomáhá-li to, necháme problém několik hodin či dní uležet a znovu zkusíme debugging a nezabere-li to, můžeme začít psát program pěkně od začátku...
To by bylo vše. Pište a pište vlastní programy a nebudete-li si s nějakou
chybou vědět rady, pročtěte si znova tuto lekci.
DCV: Odhalte všechny chyby v tomto kratičkém programu :
program PrikladNaSeznam; {Program vytvoří oboustranně zřetězený seznam a umožní jeho úpravy)} type PSeznam = ^TSeznam; TSeznam = record Jmeno : string; Vek : Byte; Predchozi : PSeznam; Dalsi : PSeznam; end; var Hlavicka,Soucasny : PSeznam; Seznam : TSeznam; ZJmeno : string; ZVek : Byte, r : char; procedure Inicializace; {Vytvoří si jakési držadlo} begin New(Hlavicka); {Vytvoříme novou dynamickou proměnnou, na kterou bude ukazovat Hlavicka} Hlavicka^.Dalsi:=Hlavicka; {Nic dalšího zatím není, tak ať ukazuje na sebe - kruhový seznam} Hlavicka^.Predchozi:=Hlavicka; Hlavicka^.Jmeno:='nikdo'; Hlavicka^.Vek:=0; {Tím bychom měli hotové jakési držadlo} Soucasny:=Hlavicka; {na které teď ukazuje Soucasny} end; procedure Zadej; {Vytvoří oboustranně zřetězený kruhový seznam} begin repeat Write('Jméno : '); Readln(ZJmeno); if ZJmeno <> '' then begin Write('Věk : '); Readln(ZVek); New(Soucasny^.Dalsi); {Nová dynamická proměnná} Soucasny^.Predchozi:=Soucasny; {Ukazatel Predchozi nové proměnné je nastaven na současnou hodnotu Soucasny} Soucasny:=Soucasny^.Dalsi; {Ukazatel současný přesměrujeme na novou proměnnou} Soucasny^.Jmeno:=ZJmeno; Soucasny^.Vek:=ZVek; Soucasny^.Dalsi:=Hlavicka; {Ať se nám kruh neporuší} Writeln; end; until ZJmeno=''; end; procedure Vypis; {Vypíše celý seznam} begin Writeln('Celý seznam :'); Soucasny:=Havicka; repeat Soucasny:=Soucasny^.Dalsi; Writeln(Soucasny^.Jmeno:40,Soucasny^.Vek:20); until Soucasny^.Dalsi=Hlavicka; Writeln('To je vše'); end; procedure Zmen; {Vypíše všechny prvky a u každého se zeptá na možnost změny} var c:char; begin Writeln('Celý seznam :'); Soucasny:=Hlavicka; repeat Soucasny:=Soucasny^.Dalsi; Writeln(Soucasny^.Jmeno:20,Soucasny^.Vek:20, ' Změnit(A/N):'); Readln(c); c:=UpCase(c); if C='A' then begin Write('Nové jméno :'); Readln(ZJmeno); Write('Nový věk :'); Readln(Vek); Soucasny^.Jmeno:=ZJmeno; Soucasny^.Vek:=ZVek; end; until Soucasny^.Dalsi=Hlavicka; Writeln('To je vše'); end;
procedure Odstran; {Odstraní vybrané prvky ze seznamu} var Smaz:PSeznam; c :char; begin Writeln('Celý seznam :'); Soucasny:=Hlavicka; repeat Soucasny:=Soucasny^.Dalsi; Writeln(Soucasny^.Jmeno:40,Soucasny^.Vek:20,'Odstranit(A/N)'); Readln(c); c:=UpCase(c); if C='A' then begin Smaz:=Soucasny; Soucasny^.Predchozi^.Dalsi:=Soucasny^.Dalsi; {Predchozi prvek ukazuje na prvek za mazaným} Smaz^.Dalsi:=Smaz^.Predchozi;{Následující prvek ukazuje před mazaný} Soucasny:=Smaz^.Predchozi; {Soucasny, jako by tam mazaný prvek vůbec nebyl} Dispose(Smaz); {Teprve nyní můžeme proměnnou vymazat} end; until Soucasny^.Dalsi=Hlavicka; Writeln('To je vše'); end;
procedure Pridej; {Přidá nový prvek do seznamu - před vybraný prvek} var Novy:PSeznam; c :char; begin Writeln('Celý seznam :'); Soucasny:=Hlavicka; repeat Soucasny:=Soucasny.Dalsi; Writeln(Soucasny^.Jmeno:2,Soucasny^.Vek:20,'Přidat před něj nový(N/N)'); Readln(c); c:=UpCase(c); if C='a' then begin Write('Nové jméno :'); Readln(ZJmeno); Write('Nový věk :); Readln(ZVek); New(Novy); Novy^.Jmeno:=ZJmeno; Novy^.Vek:=ZVek; Novy^.Dalsi:=Soucasny; Novy^.Predchozi:=Soucasny^.Predchozi; Soucasny^.Predchozi:=Novy; Novy^.Predchozi^.Dalsi:=Novy; end; until Soucasny^.Dalsi=Hlavicka; Writeln('To je vše'); end; begin Inicializace; repeat writeln('Co chcete dělat : '); writeln; writeln('V - Vytvořit seznam'); writeln('Z - Zobrazit seznam'); writeln('O - Opravit údaje'); writeln('S - Smazat některá data'); writeln('P - Přidat nové prvky'); writeln('K - Končit'); Readln(R); R:=UpCase(R); case R of 'V': Zadej; 'Z': Vypis; 'O': Zmen; 'S': Odstran; 'P': Pridej; end; until R='k'; end.
Je jich tam docela dost... A nezapomeňte všechno řádně otestovat - obzvláště zadávání, poté zobrazení, úpravu hodnot, mazání
a přidávání... Nejste-li si jisti u ukazatelů, raději si nakreslete
diagramy.
Správné řešení se nachází v některé z předchozích lekcí :) Můžete se podívat, jestli jste našli všechny chyby.
Pro dnešek je to vše.