[Logo der Universität Bayreuth]
Universität Bayreuth

Mathematisches
Institut



 Einleitung

 Erste Schritte

 Mail und News

 Drucken

 KDE

 LaTeX/TeX

 Linksammlung

 Linuxtools

 Netzwerk

 Programmieren

 Windows

 X Window

 Anträge

 Kontakt

Programmieren <-

Fehlersuche mit dem Debugger

Beim Programmieren muss man immer mit Fehler rechnen, weil sie fast immer auftreten, wenn man sich vertippt oder was auch immer. Wir werden uns hier mit diesem Problem beschäftigen. Zuerst werden wir einige Tipps und Konventionen kennenlernen, damit wir von vornherein einige "dumme" Fehler vermeiden können. Danach werden wir uns mit der Fehlersuche beim Programmieren in C++ beschäftigen. Dabei werden wir Werkzeuge kennenlernen, die uns unter Linux helfen, diese Fehler aufzuspüren.

Inhaltsverzeichnis

  1. Programmiertipps
  2. Fehlerarten und typische Fehlerquellen
  3. Fehlersuche ohne Debugger
  4. Allgemeines zum Debugger
  5. Links und Bücherempfehlung
  6. Fehlersuche mit gdb
  7. Fehlersuche mit GVD
  8. Fehlersuche mit der Bloodshed Dev-Cpp-IDE
  9. Fehlersuche mit DDD (Linux)
  10. Fehlersuche mit der Watcom IDE
  11. Homepages und Download von Debuggern
top top

Programmiertipps

  • Deklarationen sollten nach Möglichkeit am Beginn eines Blocks stehen. Funktionen sollen vor der main-Funktion deklariert (durch Funktionsprototypen) oder komplett definiert sein. Die Variablendeklarationen sollten blockweise gemacht werden, so dass man dies sehr schnell findet, wenn ein Fehler auftritt. Definieren Sie nicht die Variablen irgendwo im Programm, wenn Sie diese brauchen.
    Zum Beispiel :

              double a,b,c;
              int f,g;
              a=2+d*c;
              ...
              ...
              b=a+g+2*f;
             
             double a,c,d;
             a=2+d*c;
             ...
             ...
             int f,g;
             b=a+g+2*f;
             a=2+d*c;
            
    guter Code, alle Variablen sind blockweise definiert. schlechter Code, die Definitionen von f und g sind schlecht zu finden

  • Rücken Sie jeden Block Ihres Codes um zwei Leerzeichen (oder ein Tabulatorzeichen) gegenüber dem umgebenden Block ein.

  • Schreiben Sie nur eine Anweisung in eine Zeile und fügen Sie nach ein bis drei zusammengehörigen Anweisungen eine Leerzeile ein.

  • Kommentieren Sie so viel wie möglich.

  • Verwenden sie Funktionen zur Untergliederung des Codes in Teilprobleme.

  • Halten Sie Ihre Funktionen und Methoden klein.

  • Setzen Sie lieber zu viele Klammern in Ausdrücken als zu wenige.

  • Vermeiden Sie globale Variablen, arbeiten Sie stattdessen mit global definierten Konstanten oder Argumenten für Funktionen/Methoden.

  • Vermeiden sie Sprungbefehle wie goto, continue, break, ...).

  • Setzen Sie hinter die Kontrollanweisungen if, else, while, for und do immer einen vollständigen Block, also mit den geschweiften Klammern.


  • Schreiben Sie die Konstantennamen mit Großbuchtaben.
    zum Beispiel:
      const int N=10;
         


top top

Fehlerarten und typische Fehlerquellen


Beim Programmieren spricht man von syntaktischen und semantischen Fehlern. Die syntaktischen sind die, die auftreten, wenn man sich bei Schlüsselwörter oder Variablennamen vertippt hat. Die anderen Fehler, die das Ergebnis des Programms verändern können, sind die semantischen. An diesen zwei Fehlertypen kann man verschiedene Fehlerarten erkennen.

semantische Fehler

Man spricht von einem Designfehler, wenn der Code zu einem anderen Verhalten führt, als der Programmier beabsichtigte.
Beispiel:
           
for (int i=0;i <5;i++) a=i+2; /* Die for-Schleife bezieht sich nur auf diese Anweisung. */ cout<<"Ergebnis = "<<a<<endl;
Oder
           while (var<10); /* Hier gibt es ein Semikolon zu viel. Die while-Schleife ist leer. */
             var+=1;
           cout<<var<<endl;
          

Datenveränderung (Seiteneffekte)

In seltenen Fällen ist dieser Effekt durch Wechselwirkungen mit anderen Programmen bedingt. Unter Linux ist dieses Problem weniger zu befürchten , da die Prozesse streng voneinander abgetrennt sind und das Betriebssystem bereits verhindert, dass sich zwei Anwendungen ins Gehege kommen. Manchmal kann aber auch Ihr Programm selbst daran schuld sein; die häufigsten Ursachen sind Überschreitungen von Feldgrenzen, Überlauf von Variablen und fehlerhaftes Verwenden von Zeigern.

bedingte Kompilierung

Man baut sehr oft Ausdruckanweisungen im Programm bei der Fehlersuche ohne Benutzung eines Debugger. Diese Ausdruckanweisungen sollen aber nicht in der endgültigen Version enthalten sein. Das kann so aussehen:

top top

    ...
    #ifdef DEBUG
    cout<<"Ergebnis = "<<var<<endl;
    #endif
    ...
    
Wenn Sie dann ein abschliessendes "#endif" vergessen zu löschen und ein weiteres stehen haben, kann ein Teil des Programmes nicht ausgeführt werden.

Rechen-und Rundungsfehler

Diese treten meistens auf und sind nur schwer zu verhindern. Sie können beispielsweise Auslöschungen bewirken, die bei Verknüpfungen von sehr großen und sehr kleinen Zahlen miteinander auftreten. Vertauschung von Variablen innerhalb eines nicht assioziatives Ausdruckes kann auch zu einem Fehler führen. Oder auch bei Überläufen, da ganzzahlige Datentypen nur einen begrenzten Wertebereich haben.
zum Beispiel
            ...
           short int i,j,k;
           i=32700;
               /*
                  Mit dieser Rechnung erhalten wir j=-32768,
                  aber wir wissen,dass die Summe zweier positiver Zahlen
                  nicht negativ sein kann.
               */
           j=i+68;
           ...
           
Auch mit dem Mischen von Werten mit und ohne Vorzeichen (signed und unsigned), z.B. in Vergleichen, muss man sehr vorsichtig sein.

Fehler mit Zeigern und dynamischer Speicherverwahltung (Abstürze und core-Datei)

Die häufigsten Ursachen von Totalabstürzen sind Fehler beim Umgang mit Zeigern oder der dynamischen Speicherverwahltung, Zuweisungen auf Nullzeiger, ... . Obwohl dieses Problem nicht so oft unter Linux wie unter Windows auftritt, wird unter Linux bei jedem Absturz ein Speicherauszug (core dump) erstellt. Diesen können Sie dann in den Debugger laden und damit die genaue Stelle des Abstürz erfahren.








top top

Fehlersuche ohne Debugger

Fehler kann man auch im Programm suchen ohne Benutzung eines Debugger, indem man printfs oder couts im Programm einbaut. Mit diesen ausgaben kann man das Verhalten des Programms folgen und die genaue Stelle des Fehlers finden.
Man kann auch die "bedingte übersetzung" benutzen. Diese sind Anweisungen, die in bestimmten Fällen vom Compiler ignoriert werden.
Ein Beispiel
       ...
        #ifdef DEBUG
       cout<<Hier ist ein Fehler<<endl;(oder eine beliebige andere Anweisung)
        #endif
       ...
   
Hier werden die Anweisungen zwischen # ifdef und # endif übersetzt nur wenn die Varialbe DEBUG definiert ist, und dies dies kann im Code (mit # define)oder bei der Kompilierung erfolgen.Damit kann man einige Teile des Programm prüfen und andere ignorieren. Aber Diese bedingte Übersetzungen köauch Fehler verursachen. (siehe bedingte Kompilierung )







top top

Allgemeines zum Debugger

Beim Entwickeln und Testen Ihrem Programm ist es wichtig, daß Sie evtl. darin enthaltene Fehler finden und beheben können. Der Begriff Debugger kommt von Bug. Ein Bug (Englisches Wort ,bedeutet auf Deutsch Käfer ) in einem Programm ist ein Fehler, den Sie beheben wollen. Sie verwenden den Debugger, wenn ein Fehler aufgetreten ist oder Sie die Ausführung Ihrem Programm überwachen müssen. Der Debugger unterstützt Sie bei der Fehlersuche, sie können damit Ihr Programm schrittweise durchlaufen und den Inhalt von Variablen überprüfen.
Es gibt verschiedene Debugger unter Windows und Linux. Der verbreitetste unter Linux ist der gdb (Gnu DeBugger). Der gdb ist sehr leistungsfähig aber, weil er nur eine Kommandozeile hat, ist er schwer zu bedienen. Um die Benutzung der gdb zu erleichtern wurde der ddd (Data Display Debugger) entwickelt. Der ddd ist eine grafische Benutzerschnittstelle zum gdb aber nicht die einzige (es gibt auch der xxgdb, der tgdb, der SNIFF-Debugger und viele andere). Der ddd ist einfach populärer wegen seiner Fähigkeit, komplexe Datenstrukturen als Graphen zu visualisieren. Der ddd erspart dem Benutzer die Eingabe den gdb-Befehle auf der Kommandozeile und beschränkt die Bedienung auf Mausklicks.
Unter Windows gibt es auch gute Debugger wie beispielsweise der watcom-Debugger.

Glossar (Definitionen)

Debugging
Debugging ist der Vorgang der kontrollierten, schrittweisen Programmausführung. Man überprüft die einzelnen Anweisungen des Programms und beobachtet, wie sich Variablen und Speicherstellen verändern. Dabei finet man Die evtl. Fehler und versucht man, die zu entfernen.
Breakpoint
Ein Breakpoint (Haltepunkt auf Deutsch ) ist eine im Sourcecode anhand von Zeilennummern, Funktions- oder Maschinenadressen wählbare Stelle, an der ein Programmlauf automatisch vom Debugger unterbrochen wird. Sie können sich nun die Werte einzelner Variablen oder Objekte ansehen, die Argumente der jewiligen Funktionen untersuchen, in einzelnen Schritten weiterlaufen oder das Programm fortsetzen.
Watchpoint
Ein Watchpoint funktioniert ähnlich wie ein Breakpoints. Das Programm wird auch unterbrochen aber nur, wenn sich der Wert eines Vraiable, eines Attributs oder eines Ausdrucks verändert.
Toolbar
Die Toolbar ist eine Leiste von kleinen Icons, die meist unterhalb der Menüleiste residiert und schnellen Zugriff auf wichtige Funktionen bietet.
top top

Links und Bücherempfehlung

->
GDB: Homepage (bei GNU) bzw. Homepage (bei Redhat) und Manualseite (von GNU)
->
DDD: Homepage (bei GNU), Projektseite (bei Sourceforge) und Manualseite (von GNU)
->
Liste eigenständiger Debugger
->
Thomas Wieland: C++-Entwicklung mit Linux. dpunkt.verlag,
2., aktualisierte und erweiterte Auflage, 2002, 586 Seiten
vgl. insbesondere den Abschnitt "Fehlersuche mit dem Debugger"
Dr. Thomas Wieland ist ehemaliger Mathematikstudent und Mitarbeiter an der Uni-Bayreuth.
top top

Robert Baier ([e-mail-Adresse von Robert Baier])
© 2003 Robert Baier;
© 1999-2002 Robert Baier, Sascha Herrmann
Debugger-Seiten: © 2003 Robert Baier, Ekue-sse Situ Tomety
[Seitenzähler] Last modified: 22.07.2015