65. Fehlermeldungen von Bibliotheksfunktionen

Im Rückkehrwert der meisten Bibliotheksfunktionen ist nur kodiert, ob “es” funktioniert hat oder nicht. Der konkrete Grund des Scheiterns steht in errno, definiert in errno.h.

Was errno wirklich ist, ist implementationsabhängig. Früher mal war es einfach ein int, mittlerweile kann auch etwas Komplizierteres dahinterstecken. Grund dafür sind vor allem threads, in denen ein Prozess mehrere Dinge quasi-gleichzeitig erledigen kann. Weil aber in einem Prozess ein Variablenname immer einer Speicheradresse zugeordnet ist, könnte es passieren, dass ein Thread einen Fehler auslöst, dann ein anderer Thread zum Zug kommt, ebenfalls einen Fehler auslöst und damit errno setzt und so der erste Thread eine falsche errno sieht.

All das ist für euch aber nicht mehr relevant, ihr könnt mit errno umgehen wie mit einem int.

Scheitert eine Bibliotheksfunktion, setzt sie errno auf einen Wert. Dabei sollte dokumentiert sein, welche Werte sie setzen kann, und zwar in Präprozessorsymbolen, die auch in errno.h definiert sind und die alle mit “E” anfangen: EPERM, ERANGE, EAGAIN usf.

Will man einfach nur eine Fehlermeldung ausgeben, helfen char *strerror(int errnum) (aus string.h) und void perror(char *msg) (aus stdio.h). Erstere wandelt errno in einen (sprachspezifischen) String um, zweitere gibt die passende Fehlermeldung mit vorangestelltem msg aus.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>

int main(int argc, char **argv)
{
  setlocale(LC_ALL, "");
  if (!malloc(2000000000)) perror(argv[0]);
  if (!fopen("xxy.yxx", "r")) perror(argv[0]);
  if (!fopen("/etc/junk", "w")) perror(argv[0]);
  if (rename("errdemo.c", "/tmp/errdemo.c"))
    perror(argv[0]);
  return 0;
}

Ich setze hier voraus, dass ihr nicht wirklich 2 Gigabyte Speicher allozieren könnt, dass die Datei xxy.yxx nicht existiert, dass ihr nicht auf /etc schreiben könnt (das sollte besser so sein) und dass /tmp und euer Arbeitsverzeichnis auf zwei verschiedenen Filesystemen liegen.

Dass perror immer argv[0] übergeben bekommt, liegt an der Konvention, dass Unix-Programme bei Fehlermeldungen möglichst den Namen des Programms, das den Fehler verursacht hat, mit ausgeben sollten. Weil das ein bisschen ein Tanz ist, wenn die Fehler in tieferen Funktionen auftreten (ihr wollt bestimmt nicht argv[0] durch alle Funktionen durchziehen), definiert die glibc (im Gegensatz zu den meisten anderen C-Bibliotheken) noch char *program_invocation_name und char *program_invocation_short_name, die global den vollständigen Pfadnamen und den Basisnamen des aufrufenden Programms enthalten. Es ist nicht schwierig, entsprechendes nachzuschreiben.

Das Ergebnis:

examples> ./errdemo
./errdemo: Cannot allocate memory
./errdemo: No such file or directory
./errdemo: Permission denied
./errdemo: Invalid cross-device link
examples> export LANG=de_DE
examples> ./errdemo
./errdemo: Nicht genügend Hauptspeicher verfügbar
./errdemo: Datei oder Verzeichnis nicht gefunden
./errdemo: Keine Berechtigung
./errdemo: Ungültiger Link über Gerätegrenzen
hinweg


Markus Demleitner

Copyright Notice