Fouten: computerprogramma's barsten ervan (soms vrij letterlijk). Eén van de bezigheden tijdens het programmeren is het opsporen van fouten in je broncode. Dit kan lastig en tijdrovend werk zijn, maar gelukkig is er een gereedschap dat het allemaal een stuk makkelijker voor ons maakt: de debugger.
Elke IDE gebruikt een andere debugger. Hoewel de meeste debuggers ongeveer dezelfde functies ondersteunen, zijn de details - bijvoorbeeld de sneltoetsen - vaak net iets anders. Deze les gaat uit van de debugger van Borland.
Met de debugger kun je een programma in stappen uitvoeren. We nemen het onderstaande programma als uitgangspunt.
/* Programmeur: dhr. Ronkes Agerbeek Programma: Fouten opsporen Omschrijving: Een programma fol vouten. */ #include <iostream> using namespace std; // vermenigvuldigt twee getallen met elkaar // getal1: het eerste getal om mee te vermenigvuldigen // getal2: het tweede getal om mee te vermenigvuldigen // returns: het product van de parameters int Vermenigvuldig(int getal1, int getal2) { // bereken het antwoord int myAnswer = getal1 + getal2; // geef antwoord terug return myAnswer; } // het programma start hier void main() { // vermenigvuldig twee getallen cout << Vermenigvuldig(4, 3); }
Druk op F8 om dit programma te starten. Je ziet nu het venster van het programma verschijnen, maar het programma staat stil. Bovendien laat de IDE je code zien.
Het groene pijltje in de linkerkantlijn geeft aan welke stap de debugger straks gaat uitvoeren. In dit geval start de debugger in main. Druk op F8 deze stap te nemen.
Het pijltje springt nu naar de volgende statement. Merk op dat de debugger de accolade en het commentaar overslaat. Dit is code die de debugger niet kan uitvoeren, dus hij staat er ook niet bij stil.
De volgende opdracht voert een berekening uit en schrijf het antwoord naar het scherm. Voer deze opdracht ook uit met een druk op F8. Als je nu naar je venster kijkt, zie je dat het antwoord op het scherm staat.
Druk nog een keer op F8 om het programma af te maken en te sluiten.
Het is je misschien opgevallen dat de functie Vermenigvuldig wel is uitgevoerd, maar dat we er niet doorheen zijn gelopen. Start het programma nogmaals met F8 en druk nog een keer op F8 om main te starten. Het groene pijltje staat nu weer voor het statement dat de berekening uitvoert.
Druk nu op F7. Het groene pijltje springt naar het begin van de functie en nu kunnen we met F8 door de functie heen lopen.
Kortom, met F8 stap je over een statement heen, met F7 stap je een statement in. Als je een statement niet in kunt stappen - int Answer = getal1 + getal2; valt niet echt binnen te stappen - dan gedraagt F7 zich net zo als F8.
Zeker bij grote programma's is het niet prettig als we telkens weer door het hele programma heen moeten lopen. Daarom kun je aangeven waar een programma moet stoppen zodat je de debugger kunt gebruiken. Zo'n punt waar het programma moet stoppen, noemen we een breakpoint.
Zet de cursor op de return-statement en druk op F5. Je ziet nu een rode stip voor de regel verschijnen. Deze stip geeft aan dat je hier een breakpoint hebt geplaatst.
Start nu het programma zoals je dat normaal gesproken zou doen, dus met F9. Het programma start, maar houdt halverwege op. De groene pijl staat nu op de regel waar je een breakpoint hebt ingesteld. Je kunt nu het programma verder stap voor stap uitvoeren of je kunt de rest van het programma uitvoeren door weer op F9 te drukken.
Je kunt het breakpoint verwijderen door je cursor weer op de regel te plaatsen en nogmaals op F5 te drukken.
Nu we een programma op elk moment kunnen laten stoppen, gaan we eens kijken hoe we met de debugger informatie over ons programma op kunnen vragen. Plaats een breakpoint op het eerste statement van de functie Vermenigvuldig en start het programma.
In deze functie wordt de vermenigvuldiging uitgevoerd, maar ergens gaat het fout. Om de fout op te sporen, willen we de waarde van myAnswer in de gaten houden, terwijl we de functie stap-voor-stap uitvoeren. Dit kunnen we doen door een zogenaamde watch toe te voegen. Druk op Ctrl-Alt-W om het Watch venster tevoorschijn te halen.
Dubbelklik in het Watch venster. Je krijgt nu een dialoogvenster voor je waarin je aan kunt geven welke variabele je bij wilt houden. Typ in het vak Expression de naam myAnswer. myAnswer verschijnt nu in het Watch venster.
Het eerstvolgende statement zal de bereking uitvoeren. Druk of F8 en let op wat er in het Watch venster gebeurt. Je ziet de waarde van myAnswer verspringen. Je kunt ook meteen zien dat de waarde niet is wat hij moet zijn. Hier zit dus de fout.
Je kunt een watch eventueel verwijderen door erop te klikken in het Watch-venster en op Delete te drukken.
Als we ergens een breakpoint plaatsen, dan weten we natuurlijk precies in welke functie we zitten als het programma stil staat. Meestal willen we niet alleen weten in welke functie we zitten, maar ook welke andere functie die functie heeft aangeroepen. We nemen even een ander voorbeeld.
/* Programmeur: dhr. Ronkes Agerbeek Programma: Context Omschrijving: Als er maar functies in aangeroepen worden. */ #include <iostream> using namespace std; // berekent de afgelegde afstand als beginsnelheid, versnelling en duur gegeven // zijn // speed: de beginsnelheid // acceleration: de versnelling // duration: de tijd dat de versnelling duurt // returns: de afgelegde afstand float Race(float speed, float acceleration, float duration) { // bereken afstand float myAnswer = speed * duration + 0.5 * acceleration * duration * duration; // geef antwoord terug return myAnswer; } // vraagt om snelheid en acceleratie van Schumacher en berekent afgelegde // afstand // lengte: de duur van de race // returns: de afstand die Schumacher heeft afgelegd float Schumacher(float duration) { // declareer variabelen float mySchumacherSpeed, mySchumacherAcceleration, mySchumacherDistance; // vraag om beginsnelheid van Schumacher cout << "Wat is de beginsnelheid van Schumacher? "; cin >> mySchumacherSpeed; // vraag om versnelling van Schumacher cout << "Wat is de versnelling van Schumacher? "; cin >> mySchumacherAcceleration; return Race(mySchumacherSpeed, mySchumacherAcceleration, duration); } // vraagt om snelheid en acceleratie van Montoya en berekent afgelegde // afstand // lengte: de duur van de race // returns: de afstand die Montoya heeft afgelegd float Montoya(float duration) { // declareer variabelen float myMontoyaSpeed, myMontoyaAcceleration, myMontoyaDistance; // vraag om beginsnelheid van Montoya cout << "Wat is de beginsnelheid van Montoya? "; cin >> myMontoyaSpeed; // vraag om versnelling van Montoya cout << "Wat is de versnelling van Montoya? "; cin >> myMontoyaAcceleration; return Race(myMontoyaSpeed, myMontoyaAcceleration, duration); } // het programma start hier void main() { // declareer variabele float mySchumacherResult, myMontoyaResult; float myRaceTime; // vraag om lengte (tijd) van de race cout << "Hoe lang duurt de race? "; cin >> myRaceTime; // bereken afstand die Schumacher aflegt mySchumacherResult = Schumacher(myRaceTime); // bereken afstand die Monotoya aflegt myMontoyaResult = Montoya(myRaceTime); // is Schumacher verder gekomen? if (mySchumacherResult > myMontoyaResult) { // ja cout << "Schumacher wint." << endl; } // is Montoya verder gekomen? if (myMontoyaResult > mySchumacherResult) { // ja cout << "Montoya wint." << endl; } // zijn ze allebei even ver gekomen? if (myMontoyaResult == mySchumacherResult) { // ja cout << "Schumacher en Montoya zijn gelijk geëindigd." << endl; } // wacht op enter system("pause"); }
In bovenstaand programma wordt de functie Race twee keer aangeroepen: één keer vanuit Schumacher en één keer vanuit Montoya. Zet een breakpoint op het eerste statement van de functie Race. Start het programma en voer het uit totdat je bij het breakpoint komt.
Je bent nu in de functie Race belandt, maar hoe ben je daar nou eigenlijk gekomen? Dit kun je zien met behulp van de Call Stack (ook wel Stack Trace genoemd). Druk op Ctrl-Alt-S om de Call Stack op te vragen.
Je ziet een lijst met functienamen. De bovenste functie is de functie waar je nu inzit, in dit geval Race. De functie daar meteen onder, is de functie die Race heeft aangeroepen. Je ziet dat dat de functie Schumacher is. Daar weer onder ziet je dat de functie Schumacher is aangeroepen door main.
Druk nu op F9 om het programma voort te zetten. Even later kom je weer bij het breakpoint in de functie Race terecht. Als je nu kijkt naar de Call Stack, dan zie je dat Race dit keer is aangeroepen door de functie Montoya.