| Hier ist ein Artikel mit Informationen, die für einige
Leute sicher trivial sind, aber die erstaunlich vielen Softwareentwicklern
nicht bekannt sind:
Selbstidentifizierende Fehler:
Es gibt in ANSI-C einen oft nicht benutzten Mechanismus, viele Fehler auch
ohne Debugger aufzuspüren: Das Assert Macro. Ein Teil des Problems liegt
in Deutschland wohl auch im Namen: Assert ist ein Wort, welches im allgemeinen
im Englisch an der Schule nicht benutzt wird. int readFile(
char * Filename
char * buffer
)
{
int filehandle;
assert( filename != NULL);
assert(strlen(filename) > 0);
assert(strlen(filename) < MAX_FILENAMELEN);
filehandle = open(filename, READ_ONLY);
...
}
Falls jetzt irgend jemand meine Funktion mit falschen Parametern aufruft, z.B. mit einem leeren String, passiert während des Programlaufes folgendes: assertion failed in File readfile.c, line 34: strlen(filename) > 0 program aborted Der Tester, wer immer das sei, kann in der angegebenen Datei die Zeile aufspüren, in der der Fehler auftrat, und erfährt dann, dass die Funktion readFile mit einem Leerstring aufgerufen wurde. Oft hat man damit schon die Fehler, in den anderen Fällen zumindestens einen Anhaltspunkt.
Frage: Sollte man nicht eine wasserdichte Fehlerbehandlung machen? Das Assert hat auch seinen Sinn, wenn es nicht feuert: Bei einer Fehlersuche kann ich sicher sein, dass ein bestimmtes Problem nicht aufgetreten ist, dass ich den Fehler also woanders suchen muss. Nicht zuletzt teile ich den armen Leuten, die mein Programm einmal warten müssen, etwas über meine Intention mit: Der Pointer darf kein NULL-Pointer sein, der Dateiname soll weniger als X Buchstaben haben, ... Das alles kann man auch in Kommentaren schreiben, aber nur Coding lügt nicht.
Frage: In C++ und Java gibt es doch den Exception Mechanismus
Frage: Wenn diese Fehlermeldung dann im laufenden Betrieb beim Kunden
auftritt, ist das doch eher peinlich. Und das Programm wird doch bestimmt
größer und langsamer? Das heisst doch, nacher muß ich alle
assert wieder rausnehmen? #define NDEBUG oder in der Makedatei -DNDEBUG so wird für das assert kein Code erzeugt, so als ob man alle assert Zeilen rausediert hätte. Wie funktioniert das eigentlich ? Das Geheimnis liegt im Zusammenspiel von Preprocessor und Compiler. Das Assert ist als Macro implementiert, im allgemeinen in der Datei assert.h. Das Grundgerüst ist folgendes: #ifdef NDEBUG #define assert(exp) #else #define assert(exp) \ ( (exp) ? (void) 0 : _assert(#exp, __FILE__, __LINE__)) #endif
Das sieht doch reichlich kryptisch aus, ist jedoch gar nicht so kompliziert:
Macros sind Textersetzer. Mittels #ifdef...#else...#endif wird dafuer gesorgt,
das nur Code erzeugt wird, falls das Symbol NDEBUG nicht definiert ist. Dann
wird nämlich assert zusammen mit seinen Argument durch einen leeren
String ersetzt, der Compiler bekommt die betreffenden Zeilen nicht einmal
zu Gesicht. Im Debugfall setzt der Compiler die Wörter __FILE__ und
__LINE__ durch die aktuelle Quelldatei (nicht assert.h, sondern die der
Aufrufstelle) und die aktuelle Zeilennummer. Der Ausdruck #exp wird durch
den aktuellen Ausdruck als String ersetzt. assert(strlen(filename) < 0); die Zeile ((strlen(filename) > 0) ? (void) 0 : _assert("strlen(filename > 0", __FILE__, __LINE__));
Die tief im Inneren einer Libary definierte Funktion _assert() gibt die Argumente, eventuell mit printf() auf den Bildschirm aus und bricht das Program ab. Frage: Alles schön und gut, aber ich programmiere mit einem Fenstersystem, manchmal programmiere ich auch Anwendungen ganz ohne Bildschirm. Was nun ? Viele Benutzer haben Glück: In einigen Entwicklungsumgebungen gibt es ein assert, das ein Pop-Up-Fenster öffnet. Ansonsten lohnt es sich für solche Fälle ein assert selbst zu schreiben. Wirklich! Das prinzipielle Vorgehen ist dabei, eine Ausgabefunktion zu schreiben, die die betreffende Ausgabe irgendwohin ausgibt, die Datei assert.h zu kopieren und statt des _assert die eigenen Ausgabefunktion aufzurufen. die Ausgabe kann in eine Logdatei, über die Soundschnittstelle, als Email, als Fax, oder als was auch immer erfolgen. Ähnlich ist auch in anderen Programmiersprachen zu verfahren: meistens bieten die Hersteller irgendeine Entsprechung zu __FILE__ und __LINE__ an, selbst wenn es nicht der offizielle Sprachstandard ist. Frage: Das ist ja toll, was man mit Macros alles machen kann, ich habe da gerade eine Idee... Vorsicht: Wer Macros einsetzt, um ein paar Zeilen Code zu sparen, wird es irgendwann bitter bereuen. Ich habe mal bei einer Firma ein Macro entdeckt, das dazu dienen sollte, Teile eines Records in einen anderen zu kopieren. Dieses Macro war schon mehrere Jahre bei dieser Firma im Einsatz, leider war es nur manchmal richtig. Ich habe mit mehreren anderen Entwicklern lange vor dem Macro gesessen, um herrausfinden, was es eigentlich tat. Es arbeitete ganz gut, je nach dem mit welchen Record man es aufrief überschrieb es aber einen verbotenen Speicherbereich. Das Macro ist bestimmt für etliche Abstürze, auch bei ausgelieferten Systemen, verantwortlich. Die Firma war übrigens kurze Zeit später pleite...
home - contact - q-zone - dev-zone Copyright © 1997-2001 by Karsten Meier. All Rights reserved. |