2.6 Fehlerbehandlung
Gewöhnlich überprüft man bei vielen Funktionen durch den Rückgabewert, ob diese erfolgreich ausgeführt wurden. Bei den meisten Funktionen der Bibliotheken werden dabei im Falle eines Fehlers -1, ein NULL-Zeiger oder eine symbolische Konstante zurückgegeben. Dieser Rückgabewert sagt Ihnen zwar, dass ein Fehler aufgetreten ist, aber nicht genau welcher Fehler. Dabei sind die Bibliotheksfunktionen sehr viel auskunftsfreudiger, als man annehmen würde. Tritt z. B. bei der Funktion open() ein Fehler auf, gibt es mindestens zehn verschiedene Fehler, die dabei erscheinen können. Um herauszufinden, warum eine Funktion fehlschlug, müssen Sie einen Blick auf den Fehlercode der Variablen errno werfen. Diese Variable ist in der Headerdatei <errno.h> definiert. Außer dass Sie damit die Art des Fehlers ermitteln können, können Sie hiermit sogar theoretisch den Fehler umgehen. Viele Bibliotheksfunktionen können diese Variable (errno) auf einen positiven Wert setzen, wenn ein Funktionsaufruf fehlschlug.
Tipp für eine eigene Fehlerbehandlung Nur -1 und 0 für Fehler/Erfolg zu verwenden ist häufig eine Verschwendung. Funktionen, die einen Status zurückgeben, geben 1 für Erfolg, 0 für Fehler (z. B. Suchwort nicht gefunden) und negativ für Hard Error aus (errno durch Syscalls, z. B. konnte kein temporärer Speicher für eine Kopie des Suchwortes eingeholt werden). Der Vorteil liegt darin, dass nur eine statt zwei (Rückgabewert und errno) Variablen verbraucht werden.
|
Auf Linux-Systemen finden Sie die Definitionen aller Fehlercodes in der Headerdatei <errno.h>. Alle dieser Fehlercodes haben neben der (positiven) Nummer einen symbolischen Namen, die mit dem Präfix E beginnen.
Auf alle diese Fehlercodes jetzt einzugehen, wäre wohl etwas zu viel des Guten und ist im Prinzip auch hier nicht sinnvoll. Wenn Sie z. B. für die Funktion open() die Fehlercodes wissen wollen (müssen), sollten Sie die Man-Pages dazu verwenden. Ziemlich am Ende finden Sie hierbei die möglichen Fehlercodes von errno und deren Bedeutungen.
errno wird beim Programmstart standardmäßig auf 0 (ESUCCESS) gesetzt, da es keinen (echten) Fehlercode mit dieser Nummer gibt. Zur Rekonstruktion von Fehlern kann es recht sinnvoll sein, errno nach einem Fehler wieder auf 0 (oder eben auch ESUCCESS) zu setzen oder den Wert in eine lokale Variable zu kopieren. Dies deshalb, weil errno nach jedem misslungenen Systemaufruf von einem neuen Fehlercode überschrieben werden kann.
Hierzu ein einfaches Listing, mit dem Sie überprüfen können, ob eine Datei existiert oder nicht. Wenn Sie z. B. eine Datei öffnen wollen, die nicht existiert, wird der Fehlercode ENOENT zurückgeliefert. Somit könnten Sie praktisch anschließend eine solche Datei erzeugen.
/* filetest.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define TRUE 1
#define FALSE 0
#define BOOL int
static int if_exist_file( const char *filename ) {
FILE *f = fopen( filename, "r" );
if( errno != ENOENT ) {
fclose(f);
return TRUE; /* Datei existiert */
}
else
return FALSE; /* Datei existiert nicht */
}
int main(void) {
BOOL ex = if_exist_file( "testfile" );
if( ex )
printf("'testfile' existiert\n");
else
printf("'testfile' existiert nicht\n");
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o filetest filtest.c
$ ./filetest
'testfile' existiert nicht
$ cat > testfile
Hallo Welt
STRG+D
$ ./filetest
'testfile' existiert
Eine andere Möglichkeit, den Fehlercode zu verwenden, ohne diesen genauer zu analysieren, wäre die Verwendung der Funktion perror():
void perror (const char *message);
Diese Funktion gibt eine Nachricht auf die Standardfehlerausgabe (stderr) aus, die den Fehler beschreibt. Wenn Sie die Funktion mit einer Nachricht (message) aufrufen – anstatt mit NULL oder einem leeren String –, wird message plus einem Doppelpunkt und Leerzeichen noch vor dem eigentlichen String ausgegeben, der den Fehler beschreibt. perror() fügt der Ausgabe ein Newline-Zeichen und den Doppelpunkt an. Hierzu das vorherige Beispiel, mit der Funktion perror() und ohne errno.
/* filetest2.c */
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define BOOL int
static int if_exist_file( const char *filename ) {
FILE *f = fopen( filename, "r" );
if( f != NULL ) {
fclose(f);
return TRUE; /* Datei existiert */
}
else
return FALSE; /* Datei existiert nicht */
}
int main(void) {
BOOL ex = if_exist_file( "testfile" );
if( ex )
printf("'testfile' existiert\n");
else
perror("'testfile'");
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o filetest2 filtest2.c
$ ./filetest2
'testfile' existiert
$ rm testfile
$ ./filetest
'testfile' : No such file oder directory
|