Ebben a leckében a hibakeresés eszközeit tekintjük végig Code::Blocks környzetben.
A lépésenkénti végrehajtás segítségével programunknak mindig egyetlen sorát tudjuk végrehajtani. Fontos, hogy ilyenkor egy egységnek nem egy utasítás, hanem egy sor számít. Ha tehát egy sorba több utasítást írunk, ezeket mind egy lépésben fogja a gép végrehajtani.
Ennek például fontos szerepe lehet egy ciklus végrehajtásakor. A gép ugyanis a
for (int i=1; i<=10; i++) cout<<i;
programrészletet 1 lépésben, míg a
for (int i=1; i<=10; i++) cout<<i;
részletet 10 lépésben hajtja végre, bár a két rész között tartalmilag semmi különbség nincs.
Nyomkövetés közben az éppen aktuális sort annak elején lévő kis sárga háromszög jelöli.
Ha a sorban lévő utasítás egy eljárás hívása, a lépésenkénti végrehajtás kétféle változatát használhatjuk (ha a sorban nem eljáráshívás van, a két változat hatása között nincs különbség):
A nyomkövetéshez szükséges parancsokat a Debug menüben találjuk, de a legfontosabbakat az eszköztáron is elérhetjük. Az eljárások egy lépésben való végrehajtását a Next line paranccsal, az F7 funkcióbillentyű vagy a gomb lenyomásával aktivizálhatjuk (ez utóbbi kettő gyorsabb, hiszen ekkor nem kell a menüben keresgélnünk). Az eljárás utasításainak különválasztásához pedig használjuk a Step into parancsot, a gombot, vagy a Shift-F7 billentyűkombinációt. Természetesen egy hosszabb programrész nyomkövetéséhez egymás után sokszor kell lenyomnunk a megfelelő billentyűt.
Nyomkövetés közben a megfelelő ablakokban láthatjuk mind a program szövegét, mind pedig azt is, amit programunk eredményképpen ad. Az ablakok között az ALT és a Tab billentyűk együttes lenyomásával válthatunk, de megosztva a képernyőt akár egyszerre is láthatjuk őket.
Az eddig megismert két eszközzel egy program nyomkövetése elég kényelmetlen lehet, ha a megfigyelni kívánt programrészlet nem a program elején van. Ekkor ugyanis addig kell a megfelelő gombokat nyomogatnunk, míg az adott részhez el nem jutunk. Ennél sokkal gyorsabb módszer, hogy kiválasztjuk azt a sort, amelytől kezdve nyom követni szeretnénk és az előtte lévő sorokat egyszerűen egy lépésben végrehajtjuk. Ehhez először a kurzort vigyük a megfelelő sorra, majd válasszuk a Debug menü Run to cursor parancsát vagy nyomjuk meg az F4 billentyűt (természetesen ez a fontos funkció az eszköztáron is elérhető: ).
Ez a lehetőség nem csak a program elejének egy lépésben való végrehajtására használható. Nyomkövetés közben ugyanis bármely, számunkra érdektelen részt átugorhatunk ezzel a módszerrel. (Ha például egy ciklus magjában sok utasítás van és csak az utolsó utasítás eredménye érdekes, álljunk a kurzorral a ciklus végét jelző End-re és ahányszor az F4-et lenyomjuk, a gép mindig egyszer végrehajtja a ciklus magját.)
Ha már nincs szükségünk a nyomkövetésre, elindíthatjuk a program hátralévő részének rendes (értsd: nyomkövetés nélküli) végrehajtását. Ehhez válasszuk a Debug menü Continue parancsát, vagy nyomjuk le egyszerre a CTRL és F7 billentyűket, de használhatjuk az eszközkészlet gombját is.
Ha egy programot nyomkövetünk, akkor bárhogy folytatjuk is a program futását, a végrehajtás attól a ponttól folytatódik, ahol éppen tartottunk. Ha a programot ismét a legelejéről akarjuk kezdeni, akkor válasszuk a Debug menü Stop debugger parancsát vagy nyomjuk le a gombot.
A töréspont lényege, hogy a program elindítása előtt kijelölhetjük azt a sort, amelyiknél majd a végrehajtást szeretnénk megállítani. Ezután ha F8-cal (Debug: Start) futtatjuk a programot, amikor a kijelölt sorhoz ér, megszakad a végrehajtás. Innen a program futását folytathatjuk lépésenként vagy nyomkövetés nélkül.
Tulajdonképpen egy töréspont kijelölésével ugyanazt a hatást érjük el, mintha a kurzorral ráállnánk a megfelelő sorra és lenyomnánk az F4 billentyűt. A töréspont alkalmazása azonban ennél sokkal kényelmesebb, hiszen nem kell minden futtatás előtt mindig megkeresnünk ugyanazt a sort, sőt egy programban egyszerre több töréspontot is kijelölhetünk.
Több töréspont esetén természetesen azon a ponton áll meg a program, amelyet végrehajtás közben leghamarabb elért.
Egy töréspont elhelyezéséhez kattintsunk egyet a margón a kiválasztott sor sorszámától jobbra, vagy nyomjuk meg az F5 billentyűt, amikor a kurzor a megfelelő sorban van. Ekkor egy piros kör jelenik meg a margón. Ha már nincs tovább szükségünk a töréspontra, azt egy újabb kattintással tudjuk törölni.
A programunkban elhelyezett töréspontok listáját a Debug menüben a Debugging windows menüpont Breakpoints… elemét választva tekinthetjük meg. Egy töréspontot itt is ki tudunk törölni. Ehhez válasszuk ki a lista megfelelő sorát, majd a jobb egérgomb hatására felbukkanó menüből válasszuk a Remove breakpoint funkciót.
Az eddig említett töréspontokon minden esetben megszakadt a program futása. A tesztelés szempontjából viszont hasznos lehet egy olyan töréspont, amelyhez valamilyen feltételt kapcsolunk. Ekkor az ilyen töréspontokon a program futása csak akkor szakad meg, ha ez a feltétel igaz.
Ezen feltételeket a Debug menüben a Debugging windows menüpont Breakpoints… elemét választva adhatjuk meg. Válasszuk ki a megfelelő töréspontot, majd a jobb egérgomb hatására felbukkanó menüből válasszuk a Breakpoint properties funkciót. A felugró ablakban pedig írjuk be a megfelelő feltételt.
Az Ignore count before break esetében egy nem negatív egész számot adhatunk meg. Ezzel azt állíthatjuk be, hogy a kiválasztott sor hányadik végrehajtásánál akarjuk a program futását megszakítani.
A hibakeresés másik hatékony eszköze a változók tartalmának figyelése. Ezt a legáltalánosabb módon (amely minden programozási környezetben működik) úgy valósíthatjuk meg, hogy a változók tartalmát a program megfelelő helyein kiíratjuk a képernyőre. Ez azonban elég körülményes, hiszen egyrészt állandóan módosítanunk kell a programszöveget, és a futtatás előtt kell döntenünk, hogy mely változók lesznek érdekesek számunkra, másrészt egy összetettebb adat áttekinthető kiíratásához már egy kisebb programot kell írnunk.
A Debug menüben a Debugging windows menüpont Watches funkcióját választva nyomon követhetjük a lokális változók és a függvényparaméterek értékét, azok változását. A megjelenő ablakban pirossal kiemelve láthatjuk azt az elemet illetve azokat az elemeket, amelyeknek éppen módosult az értéke.
A Debug menü Edit watches pontjában további elemeket is vehetünk fel a megfigyeltek listájába.
Természetesen, ha valamilyen összetett típusú változót figyelünk, a Watches ablakban annak minden része (tehát egy tömb minden eleme, egy rekord összes mezője) megjelenik. Lehetőségünk van arra is, hogy egy ilyen struktúrának csak valamelyik részét figyeljük meg. Ilyenkor a változó megfelelő részét a c++ szabályai szerint kell azonosítanunk (azaz a változónév mellett tömbnél meg kell adnunk az elem indexét, rekordnál pedig a mezőnevet is).
A Watches ablakban a jobb egér gombbal bármelyik elemre kattintva elérjük a legfontosabb alkalmazható funkciókat.
A program tesztelése közben érdekes lehetőség az, amikor futás közben egy változó tartalmát megváltoztathatjuk. Ehhez az előbb említett módon a Watches ablakban az adott változóra kattintva a Change value... opciót kell választani.
Amikor törésponttal egy eljáráson vagy függvényen belül állítjuk meg a programot, hasznos lehet tudni, hogy milyen úton jutottunk el idáig, vagyis melyik eljárás melyik sorában volt a megfelelő eljáráshívás. A visszatérési verem ablakban (Debug menü Debugging windows pontjában lévő Call Stack) egy olyan listát kapunk, amely megmutatja, hogy a végrehajtás során melyik eljárások hol és mikor hívódtak meg.
A nyomkövetéshez szükséges információk
Ahhoz, hogy a programunkat tudjuk nyomkövetni, a fordítónak plusz információkat kell a programkódba szerkesztenie. Természetesen ezek az adatok megnövelik a program hosszát, tehát csak addig van rájuk szükség, amíg programunkban hibát keresünk. Azt, hogy kerüljenek-e a lefordított programba ilyen adatok, a Project menü Build options… pontjában állíthatjuk be a Produce debugging symbols [-g] flag segítségével. Ezt követően már csak újra kell fordítani a projektet a Build menü Rebuild funkciójával és kezdődhet a nyomkövetést.