Fehlerbehandlung
C und Fehlerbehandlung
Wer bisher Java programmiert hat, ist es gewohnt, eine strukturierte Fehlerbehandlung zu bekommen, d. h. ein von der Sprache unterstütztes Konzept, um mit Fehlern umzugehen und sie zu behandeln. C++ und Java bieten Exceptions zur Fehlerbehandlung an. C hat aufgrund seines Alters kein entsprechendes Konzept.
- Das Konzept zur Fehlerbehandlung in C lässt sich kurz zusammenfassen: Es gibt kein Konzept
- Fehler werden durch spezielle Rückgabewerte der Funktionen signalisiert
- Aufrufer muss auf den Rückgabewert prüfen und reagieren
- Fehler behandeln (Fehlerbehandlungsroutine anspringen → goto)
- Fehler ignorieren
- Fehler selbst über einen Rückgabewert weitergeben
- Zusätzlich wird eine globale Variable
errnogesetzt
Während man bei C die fehlende Fehlerbehandlung auf das Alter schieben kann, stellt sich die Frage, warum eine der modernsten Sprachen, nämlich Go (2007 erschienen), ebenfalls kein Konzept für Ausnahmen hat. Tatsächlich ist es so, dass Ausnahmen ein durchaus umstrittenes Konzept sind und nicht von allen Entwicklerinnen als die einzige Lösung für das Signalisieren von Problemen angesehen wird. Die Entwickler von Go haben sich entschieden, dass Funktionen beliebig viele Rückgabewerte haben können und dass einer davon zum Anzeigen von Fehlern verwendet wird. Sie empfanden Ausnahmen als zu schwergewichtig und fürchteten, dass es sonst wie bei Java zu einer Flut von Ausnahmen kommt, die niemand mehr behandeln kann und will.
Der folgende Code zeigt ein Beispiel dafür, wie in C mit Fehlern umgegangen wird.
if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < O) {
DieWithError("socket () failed") ;
}
/* Bind to the local address */
if (bind(servSock, (struct sockaddr *)&echoServAddr,
sizeof(echoServAddr)) < O) {
DieWithError ("bind () failed");
}
/* Mark the socket so it will listen for incoming connections */
if (listen(servSock, MAXPENDING) < O) {
DieWithError("listen() failed") ;
}
for (;;) { /* Run forever */
/* Wait for a client to connect */
if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr,
&clntLen)) < O) {
DieWithError("accept() failed");
}
}
Man sieht hier an dem C-Beispiel sehr deutlich die Probleme klassischer Fehlerbehandlung über Rückgabewerte:
- Die normale Programmlogik und die Fehlerbehandlung sind wild gemischt.
- Der Programmcode wird erheblich aufgebläht und sehr unübersichtlich, da manche IFs der Fehlerbehandlung dienen und andere wieder dem normalen Kontrollfluss.
- Der Rückgabewert von Funktionen ist doppeldeutig: Er transportiert sowohl Fehler- als auch normale Informationen.
- Es kann leicht passieren, dass man vergisst einen Fehlercode abzufragen, sodass Fehler überhaupt nicht behandelt werden.
- Es ist sehr schwer Fehler weiterzureichen, da die hier dargestellte Funktion vier verschiedene Fehlersituationen im eigenen Rückgabewert codieren müsste.
- Bei Funktionen, die einen Pointer zurückgeben (z. B.
malloc), wird der Fehler durchNULLangezeigt
⇒ Fehlercode muss auserrnogelesen werden - Bei Funktionen, die normalerweise nichts zurückgeben müssen (z. B.
pthread_create), wird der Fehlercode direkt als Rückgabewert geliefert
Fehlernummern und Fehlerausgabe
- Die Fehlernummern werden über
#definedefiniert und sind spezifisch für die aufgerufenen Funktion (siehe<errno.h>) #define EPERM 1 /* Operation not permitted */#define ENOENT 2 /* No such file or directory */#define ESRCH 3 /* No such process */#define EINTR 4 /* Interrupted system call */#define EIO 5 /* Input/output error */#define ENXIO 6 /* Device not configured */- …
- Fehlernummern können mit
strerror(int errnum)aus<string.h>in eine Fehlermeldung übersetzt werden
Beispiel für die Ausgabe eines Fehlers
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int main() {
FILE* f;
if ((f = fopen("test", "r")) == NULL) {
printf("%s\n", strerror(errno));
exit(1);
}
}
No such file or directory
Beispiel: Fehler weitergeben
#define EFOPEN 1
FILE* fd = NULL;
int openFile(const char* file) {
fd = fopen(file, "r");
if (fd == NULL) {
return EFOPEN;
}
else {
return 0;
}
}