Home » Tutorials » Programmierkonzepte » Fehlerbehandlung

Fehlerbehandlung

Die Vorbereitung

Wird ein Programm erfolgreich kompiliert, so werden die symbolischen Namen der Bezeichner entfernt. Erweiterte Compiler-Optimierungen können dazu führen, dass der Programmablauf für uns nicht mehr wirklich nachvollziehbar wird. Darum weisen wir den Compiler an, zusätzliche Debug-Informationen in die ausführbare Datei zu übernehmen und deaktivieren außerdem die Optimierungen. Die erzeugte Datei wird durch die Aufnahme der Debug-Informationen wesentlich größer. Erlangt unser Programm dann irgendwann die Reife, so können wir diese, dann unnötigen Informationen, aus dem fertigen Programm entfernen. Delphi bietet uns hier einen relativ einfachen Mechanismus an, beim Kompilieren zwischen Endversion (Release) und Analyse (Debug), zu unterscheiden. Unter dem Menüpunkt Projekt/Standardoptionen/Delphi wählen wir als Standardprojektoption die Build-Konfiguration Debug aus.

bild1.png

Neu gestartete Projekte werden nun automatisch mit Debug-Informationen kompiliert. Daran erkennen wir auch, dass sich Debug-Informationen auf Projektebene bewegen. Ändern wir Einstellungen eines bestehenden Projekts, jetzt unter Menüpunkt Projekt/Optionen zu finden, so reicht ein Kompilieren des Quelltextes durch Strg+F9 allein nicht, da dabei nur die Änderungen am Quelltext zur letzten Übersetzung berücksichtigt werden. Das Projekt muss also durch die Tastenkombination Umsch+F9 neu erzeugt werden.
Wir nehmen noch einige ergänzende Einstellungen vor:Im Unterpunkt Delphi-Compiler/Compilieren aktivieren wir unter Laufzeitfehler die Bereichs- und Überlaufprüfung.

bild2.png

Und im Unterpunkt Delphi-Compiler/Linken aktivieren wir die Debug-Informationen, damit diese in die ausführbare Datei geschrieben werden.

bild3.png

Ist die Überlaufprüfung aktiviert, so wird bei arithmetischen Integer-Operationen auf ein Über- bzw. Unterschreiten der Grenzen des Datentyps geprüft. Falls das im folgenden Konsolenprogramm so ist, wird eine Fehlermeldung erzeugt und angezeigt. Ist die Prüfung deaktiviert, so zeigt sich das kommentierte Verhalten.

program Ueberlaufpruefung;
{$APPTYPE CONSOLE}
uses
  SysUtils;
var
  IntZahl: Integer;
begin
  try
    IntZahl := High(Integer); //Maximum im Integer-Bereich
    WriteLn(IntZahl);
    IntZahl := IntZahl + 1; //Maximum + 1 => Überlauf = Minimum im Integer-Bereich
    WriteLn(IntZahl);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn;
end.

Bereiche von Datentypen können natürlich auch unterlaufen werden. Wollen wir ähnlich gelagerte Fälle mit Integer-Typen kleiner 4 Bytes ausprobieren, dann müssen wir allerdings die Bereichsprüfung und nicht die Überlaufprüfung deaktivieren, da alle arithmetischen Operationen intern mittels 32-Bit-Werten ausgeführt werden. Die Berechnung an sich läuft also fehlerfrei, nur die implizite Typumwandlung verletzt hier, beim Zuweisen des Ergebnisses an den kleineren Datentyp, die Bereichsgrenzen.

var
  ByteZahl: Byte;
begin
  try
    ByteZahl := Low(Byte); //Minimum im Byte-Bereich
    WriteLn(ByteZahl);
    ByteZahl := ByteZahl - 1; //Minimum - 1 => Unterlauf = Maximum im Byte-Bereich
    WriteLn(ByteZahl);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn;
end.

Sofern solche Überläufe nicht ausdrücklick erwünscht sind, sollten diese Prüfungen im Debug-Modus immer aktiviert sein. Insbesondere die Bereichsprüfung ist hier sehr nützlich für uns, da sie anzeigt, wenn Zugriffe außerhalb der Grenzen z.B. eines Arrays oder Strings stattfinden.

program Bereichsprüfung;
{$APPTYPE CONSOLE}
uses
  SysUtils;

procedure ZeigeArrayBereichsverletzung;
var
  intArray: array[0..9] of Integer;
  i: Integer;
begin
  for i := 1 to 10 do //Nicht Index 0, dafür Index 10 beschreiben
    intArray[i] := i;
  for i := 0 to 10 do //Index 0..9 + 10 auslesen
    WriteLn(intArray[i]);
end;

begin
  try
    ZeigeArrayBereichsverletzung;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn;
end.

Im gezeigten Beispiel wird, bei deaktivierter Bereichsüberprüfung, über die Grenzen des Arrays hinaus in den Speicher geschrieben und daraus gelesen. Was immer dort im Speicher stand, es wurde überschrieben und die Konsequenzen daraus sind nicht vorhersehbar. Zusätzlich sehen wir an Index 0 des Arrays einen uninitialisierten Wert.