Att leta efter fel i C++-programmet (debugga).

Att korrigera kompileringsfel

Vad som orsakar kompileringsfel kan ibland vara svårt att förstå. En metod är att kommentera igen och bort någon eller några satser. Kompilera igen. Om felet fortfarande kvar, kommentera bort någon annan sats istället. Annars, finns "felet" bland de bortkommenterade... Kompileringsfel som ej "går att hitta": Prova att kommentera bort någon eller några satser. Kompilera igen. Om felet fortfarande kvar, kommentera bort någon annan sats istället. Annars, finns "felet" bland de bortkommenterade...

Att hitta logiska fel i sitt program, dvs programmet fungerar ej

Med programutskrifter:

Ex. 1:
En bra metod är att lägga in lite utskrifter av variabler i den del av koden (main och/eller annan funktion) där knasigheter misstänks uppstå. Skriv ut alla variabelvärden i det kodavsnitt där fel misstänks uppstå. Skriv ut variabler före kodavsnittet, i kodavsnittet och efter kodavsnittet. För att (test)utskrifterna skall vara lätta att förstå kan de föregås av en lämplig ledtext (variabelns namn).
    x=p-r;
    ...
    y=a/b;
    ...
    z=x*sqrt(y);
    cout << "z=" << z;
Följande resonemang kan vara relevant när det visar sig att variabeln z har ett felaktigt värde: Skriv ut x och y. Om någon av dessa är fel, skriv ut p och r respektive a och b.

Ex. 2:
Om programmet räknar fel - ställ dig följande frågor: Givet de indata som jag givit programmet (exvis via cin-satser) - går programmet igenom de satser som jag tänkt mig? Exvis if-satsen, else-satsen, loopen (går den korrekt antal varv?), har funktionen blivit serverad med rätt indata (värdeparameter), ger funktionen ifrån sig rätt värde (return-sats eller referensparameter)?
Hur kan man kontrollera detta? Jo, lägg in testutskrifter (cout-satser, vanlig text och ev. utskrift av variabel-värden) i misstänkta delar av programmet så blir du varse att programmet gör rätt saker. Om du ej misstänker någon speciell del, så får du lägga in testutskrifer lite överallt, till att börja med.

Exempel:

int main() { ... if (x>4) { cout << "Test: I if-satsen" << endl; ... } else { cout << "Test: I else-satsen" << endl; ... } ... int varv=0; //Varvräknare bara för testutskrifter. while (ok==1) { cout << "Test: I while-loopen. Varv=" << varv << endl; varv++; ... } ... for (i=min; i<=max; i++) { cout << "Test: I for-loopen. i=" << i << endl; ... } ... return 0; }
Ex. 3:
Med papperssimulering
En annan metod är att på papper skriva ned vad som händer i kodavsnittet.
Antag att du läser in ett tal med ett visst värde. Vad händer i nästa sats som är en if-sats? Vilken väg väljer programmet och vad händer i nästkommande sats?
  for (i=0; i<9; i++)
   {

       cin >> tal;
       if (tal==0)
       {
             flagga=1;    //Gör något...
             ...
       }
       else
       {
             ...          //Gör något annat...
       }
   }
Ex. 4:
Med funktioner. Om exvis main anropar en annan funktion kan man göra utskrifter i main precis före anropet av funktionen och direkt efter samt även i själva funktionen.
float berakna(float x)
{
   //skriv ut in-parametern x för att kolla värdet
   cout << "x=" << x << endl; 
   ...
}

int main()
{
   ...
   //skriv ut in-parametern y för att kolla värdet
   cout << "y=" << y << endl;
   z=berakna(y);
   //skriv ut y igen och z
   cout << "y=" << y << " " << "z=" << z << endl; 
   ...
}


När programmet är färdigtestat och fungerar enligt specifikationen så tar man bort alla onödiga utskriftssatser. Eller alternativt kommenterar bort dem, de kan kan vara bra att ha senare, nya fel kan dyka upp...

Ex. 5:
Detta exempel kräver att du har läst om strängar.
Antag att du har en strängSTR som skall hanteras i koden.

   string STR;
   cout << "Ge en rad med text:" << endl;
   cin >> STR;

   for (i=0; i<STR.length(); i++)
   {
       if (STR[]=='?')
       {
             flagga=1;    //Gör något...
             ...
       }
       else
       {
             ...        //Gör något annat...
       }
   }
Tänk dig att STR exvis har värdet "Torsten?Jarkrans". Skissera sedan hur många varv loopen snurrar och vad som händer i varje varv i loopen, bokför alla inblandade variablers värden. En del i denna simulering ger vid handen att i varvet när i=7 blir if-satsen sann och då sätts variabeln flagga till 1.

Ex. 6:
Klasser i C++
För kod som använder egna klasser kan det vara bra att ha utskrifter i konstruktorer och destruktorer.

Sammanfattning

Innan man "ger upp" en programmeringsuppgift därför att programmet "ej fungerar", bör man försöka luska ut varför det ej fungerar enligt exvis ovanstående modell. Att ta reda på de logiska felen är ofta den tunga biten när ett program skall skrivas.