Hinweise und Warnungen Verhalten bei Fehlern

Fehlerbehandlung - Verhalten bei Fehlern www.delphi-treff.de 148 sind. Im Umkehrschluss können wir durch Doppelklick bzw. Quelltext anzeigen im Kontextmenü des 2. Eintrags im Aufruf-Stack, die aufrufende, dann hervorgehobene Stelle im Quelltext lokalisieren. Hier gibt es einen Unterschied zu einer VCL-Anwendung, denn dort wird die auf den Aufruf folgende Zeile markiert. Zudem wäre dort der Aufruf-Stack deutlich umfangreicher, da in einer Formularanwendung erheblich mehr Initialisierungsarbeit geleistet werden muss.

5.3. Verhalten bei Fehlern

Es ist unvermeidlich, dass Programmierer Fehler produzieren. Diese Fehler dann zu finden ist eine Sache der Erfahrung. Trotzdem gelingt es nicht immer, einen Fehler schnell zu lokalisieren und solch eine Fehlersuche kann sich auch manches Mal über Stunden hinziehen. Kein Grund zu verzweifeln: Zumeist genügt es, eine Pause einzulegen, um mit klarem Kopf die Fehlersuche wieder aufzunehmen und die eigene Strategie in Frage zu stellen.

5.3.1. Hinweise und Warnungen

Bevor es mit den Fehlern losgeht, ein paar Worte zu einem, von Einsteigern oft unterschätzten Thema: Der Compiler gibt uns nicht nur Fehlermeldungen aus, er macht uns auch auf mögliche Fehlerquellen aufmerksam. Syntaktisch kann der Quelltext also in Ordnung sein, möglicherweise enthält er jedoch Fehler in der Programmlogik. Unser Ziel muss es demnach sein, dass solche Meldungen gar nicht erst entstehen, damit potentielle Fehlerquellen von vornherein ausgeschlossen werden. Betrachten wir die vom Compiler erzeugten Hinweise folgender Funktion: function MaximumAZahl1, AZahl2: Integer: Integer; var i: Integer; H2164 begin Result := 0; H2077 if AZahl1 AZahl2 then Result := AZahl1 else Result := AZahl2; end; [DCC Hinweis] HinweiseUndWarnungen.dpr5: H2077 Auf Maximum zugewiesener Wert wird niemals benutzt [DCC Hinweis] HinweiseUndWarnungen.dpr3: H2164 Variable i wurde deklariert, aber in Maximum nicht verwendet Fehlerbehandlung - Verhalten bei Fehlern www.delphi-treff.de 149 Der untere Hinweis ist schnell abgehandelt, denn offensichtlich wurde hier nur vergessen, die Variablendeklaration von i zu entfernen. Ein Doppelklick auf diesen Hinweis im Meldungsfenster führt uns direkt zur Fundstelle im Quelltext, also zur Deklaration der überflüssigen Variable i in der angegebenen, auf die gesamte Datei bezogenen Zeile – hier und in der Folge allerdings angepasst auf den jeweiligen Textausschnitt. Markieren durch Einfachklick und Anfordern der Hilfe durch Drücken von F1 zeigen uns weitere Informationen zu diesem Hinweis, ebenfalls zu erreichen durch Aufrufen der Hilfe und suchen nach der Meldungsnummer H2164. Auch die Hinweismeldung der oberen Zeile spricht bereits für sich. Result wird in allen Fällen ein neuer Wert zugewiesen und damit ist die Initialisierung in Zeile 5 überflüssig und kann ebenfalls entfernt werden. Wie die Meldung außerdem zeigt, können innerhalb von Funktionen der Funktionsname und Result gleichrangig verwendet werden. Aus Gründen der Einheitlich- und Übersichtlichkeit verwenden wir jedoch ausschließlich Result. Während Hinweise meist stilistischer Natur sind, deuten Warnungen auf mögliche Fehlerquellen hin. function PotenzABasis: Integer; AExponent: Cardinal: Integer; var i: Integer; begin Result := 1; Fehlende Initialisierung for i := 1 to AExponent do Result := Result ABasis; Lesender Zugriff auf Result end; [DCC Warnung] HinweiseUndWarnungen.dpr8: W1035 Rückgabewert der Funktion Potenz könnte undefiniert sein Abgesehen davon, dass man sich hier um die mögliche Größe des Ergebnisses wenig Gedanken macht, wird Result nicht mit dem richtigen Wert vorbelegt. Der lesende Zugriff darauf liefert also dort einen Zufallswert und stellt einen Sonderfall einer nicht initialisierten lokalen Variable dar. Wäre die Funktion eine Prozedur und Result eine lokale Variable darin, dann würde sich folgende Warnmeldung ergeben: [DCC Warnung] HinweiseUndWarnungen.dpr8: W1036 Variable Result ist möglicherweise nicht initialisiert worden Glücklicherweise werden wir vom Compiler auf solche Schnitzer aufmerksam gemacht. Nicht immer ist jedoch direkt klar, was an unserem Quelltext eine Warnung hervorrufen sollte: Fehlerbehandlung - Verhalten bei Fehlern www.delphi-treff.de 150 function SignumAZahl: Integer: Integer; begin if AZahl 0 then Result := -1 else if AZahl 0 then Result := 1 else if AZahl = 0 then Stiftet Verwirrung Result := 0; end; [DCC Warnung] HinweiseUndWarnungen.dpr11: W1035 Rückgabewert der Funktion Signum könnte undefiniert sein Offensichtlich ist hier jeder mögliche Fall für AZahl abgehandelt worden und Result wird auch das richtige Ergebnis zugewiesen. Der Compiler erkennt jedoch in der letzten If-Anweisung eine Bedingung, für die es dann natürlich auch eine Alternative innerhalb eines Else-Zweiges geben könnte. Abhilfe schafft hier das einfache Entfernen der überflüssigen Bedingung, denn das abschließende Else behandelt alle anderen Fälle. Gerade fehlende oder falsche Else-Zweige innerhalb von If- und Case-Anweisungen führen immer wieder zu schwer lokalisierbaren Fehlern. Handelt es sich nämlich nicht gerade um einen Initialwert, dann werden wir auch nicht auf einen fehlenden oder semantisch falschen Wert in der Fallunterscheidung hingewiesen. Ist man sich hier unsicher, so könnte man einfach im abschließenden Else eine Exception erzeugen, die auf den unbehandelten Wert in der Fallunterscheidung hinweist. Häufig sieht man auch folgende Warnmeldung: var sTemp: AnsiString; sTemp: string; begin sTemp := 123 ; WriteLnStrToIntsTemp; Implizite Typumwandlung [DCC Warnung] HinweiseUndWarnungen.dpr5: W1057 Implizite String-Umwandlung von AnsiString zu string Seit Delphi 2009 verweist der Alias String nicht mehr auf einen AnsiString, sondern auf einen UnicodeString. Da die internen String-Funktionen natürlich weiterhin mit dem Alias arbeiten, wandelt Delphi den AnsiString in einen UnicodeString um, was, in diese Richtung umgewandelt, auch nicht weiter fehleranfällig ist. Trotzdem Fehlerbehandlung - Verhalten bei Fehlern www.delphi-treff.de 151 bereinigen wir den Quelltext, in dem wir mittels stringsTemp den Parameter für StrToInt explizit umwandeln oder direkt sTemp als string deklarieren. Das nackte Ergebnis der beiden Möglichkeiten ist erstmal gleich gut – die Warnung verschwindet. Typumwandlungen haben aber immer eine latente Fehleranfälligkeit, denn man muss die beiden Typen bewerten können, um zu wissen, ob hier unter Umständen Datenverlust möglich ist. Außerdem sollte man, solange kein gegenteiliger Grund vorliegt, immer mit den generischen bzw. dynamischen Typen arbeiten, denn der Compiler ist darauf optimiert. Eine Ausnahme wäre z.B. die programmexterne Kommunikation mit einer Datei, bei der fundamentale bzw. statische Typen mit ihrer konstanten Darstellungsbreite, über Delphi- Versionen hinweg, angebracht sind. Wie auch in den nächsten Unterkapiteln, können wir hier nur einige wenige Meldungen exemplarisch zeigen. Aber allein das Lesen und Verstehen der Hinweise und Warnungen sollte uns, in Kombination mit dem entsprechenden Hilfetext, zur Lösung des Problems führen.

5.3.2. Fehler zum Zeitpunkt der Kompilierung