Ich habe angefangen in C zu programmieren. Auf einem Linux PC mit gcc.
Ich habe jetzt Funktionen, die Funktionen usw. aufrufen. Wenn ich jetzt
in irgendeiner aufgerufenen Funktion z.B. eine Datei öffnen will, aber
der Rückgabewert ist eof und ich möchte das ganze Programm einfach
printf("Fehlermeldung, dies und das, bla und Blub"); und Abbruch, wie
mache ich das, ist das schlau?
Dirk K. schrieb:> Paul Schokemöhl schrieb:> wie> mache ich das,>> exit(-1);>> ist das schlau?>> Nein.
Also nicht schlau. Wie mache ich es, das es schlau ist?
Paul Schokemöhl schrieb:> ist das schlau?
Kommt drauf an, wenn dein Programm noch sinnvoll ohne die Datei
weiterarbeiten kann dann nicht, wenn es ohne die Datei zu öffnen
praktisch nichts brauchbares mehr machen kann, dann schon.
Du rufst einfach in Abhängigkeit vom Rückgabewert der entsprechenden
Funktion eine Aufräumfunktion auf, die im einfachsten Fall nur den Grund
auf dem jeweiligen Device ausgibt und anschließend das Programm mit
einem entsprechenden Rückgabewert beendet. In komplexeren Fällen führt
die Funktion dann noch Aufräumarbeiten durch.
Paul Schokemöhl schrieb:> Wenn ich jetzt> in irgendeiner aufgerufenen Funktion z.B. eine Datei öffnen will, aber> der Rückgabewert ist eof
EOF = End Of File
fopen liefert bei Fehler normalerweise 0 zurück, open und andere posix
Funktionen meistens -1. Jedenfalls nicht EOF.
Paul Schokemöhl schrieb:> ich möchte das ganze Programm einfach> printf("Fehlermeldung, dies und das, bla und Blub"); und Abbruch, wie> mache ich das, ist das schlau?
Fehlermeldungen würd ich nach stderr (standard error) schreiben:
1
fprintf(stderr,"Fehler!\n");
Oder ins system log:
https://linux.die.net/man/3/syslog
Ich schreibe manchmal auch eine eigene logging Funktion, die dann nach
stderr oder syslog schreibt.
Beenden kann man es mit mehreren Funktionen, unter anderem: abort exit
_Exit
Diese tun leicht verschiedene Dinge.
Frameworks, wie z.B. gtk, haben oft nochmal eigene logging Funktionen
usw., normalerweise mit der Möglichkeit eine eigene logging handler
funktion zu registrieren.
Dirk K. schrieb:> Paul Schokemöhl schrieb:>> wie>> mache ich das,>> exit(-1)
Warum gerade -1? Was unterstützt und sinnvoll zum Aufrufer übergeben
wird, ist systemabhängig. Ich würde positive Werte im Bereich 0 bis 127
empfehlen, das sollte auf den meisten Systemen gehen.
Wenn man sich auf ISO C beschränken will, das überall funktionieren
sollte, muss man sich auf EXIT_FAILURE und EXIT_SUCCESS beschränken.
>> ist das schlau?>> Nein.
Sagen wir mal so: Für sehr einfache Programme kann man das schon mal
machen, aber meistens ist es keine gute Idee. Es ist besser, das Problem
an den Aufrufer zurückzumelden, damit der dann geeignet reagieren kann
statt einfach das Programm "eigenmächtig" zu beenden.
Man stelle sich vor, das wird mal ein GUI-Programm, und die Routine, die
versucht, eine Datei zu öffnen, schießt gleich das ganze Programm ab,
nur weil sie die Datei nicht findet.
Man muss zusätzlich auch bedenken, dass alle Ressourcen, die ggf. in
anderen Programmteilen genutzt werden, nicht sauber freigegeben werden,
es sei denn, sie definieren dafür extra einen atexit-Handler.
Daniel A. schrieb:> Fehlermeldungen würd ich nach stderr (standard error)> schreiben:fprintf(stderr, "Fehler!\n");> Oder ins system log:> https://linux.die.net/man/3/syslog
Es ist für einen Programmieranfänger eher nicht sinnvoll, wenn seine
ersten Versuche ihre Outputs ins syslog schreiben.
fprintf(stderr,"can't open '%s': %s\n",filename,strerror(errno));
10
exit(EXIT_FAILURE);
11
}
12
...
> Ist das schlau?
Ist quick'n'dirty. Du gibst so dem Aufrufer keine Chance, den Fehler
irgendwie zu behandeln - eine untergeordnete Funktion entscheidet, ob
das Programm weiterläuft oder nicht.
Oder direkt eine Sprache verwenden, welche ein systematisches
Fehlermanagement in Form von Exceptions bietet, sodass man sich das
Herumhantieren mit Rückgabewerten spart. z.B. C++:
1
#include<stdexcept>
2
#include<stdio.h>
3
4
voidfoo(){
5
FILE*f=fopen("test.txt","r");
6
if(!f)
7
throwstd::runtime_error("Datei konnte nicht geöffnet werden");
8
9
puts("Mach was mit der Datei");
10
fclose(f);
11
}
12
13
voidbar(){
14
puts("bar(). Mache was...");
15
foo();
16
puts("bar(). Mache nochwas mehr...");
17
}
18
19
intmain(){
20
try{
21
puts("main(). Mache was...");
22
bar();
23
puts("main(). Mache nochwas mehr...");
24
return0;
25
}catch(conststd::exception&e){
26
fprintf(stderr,"Fehler: %s\n",e.what());
27
return1;
28
}
29
}
Natürlich wäre es besser hier direkt die C++-Klassen wie std::ifstream
zu nutzen statt das C-I/O über fopen() & Co. Im Idealfall hat man genau
eine Stelle mit Fehlerbehandlung, direkt in der main(), und der
restliche Code sieht so aus als gäbe es keine Fehler, indem man den
Compiler das Rückwärts-Abwickeln erledigen lässt.
@Paul Schokemöhl
Was Du willst ist eine sehr unfreundliche Art mit dem Benutzer
umzugehen.
"Du, Du, böser!" und Abbruch ist würglich unfreundlich.
DU bist es, der den Programmfluss kontrolliert.
Eine einfache Meldung: "Dateiende erreicht", von mir aus auch auf
Auswärts, mit anschließender Abfrage: "Watt nu?" wäre zumindest höflich.
Also komm mal runter von Deinem hohen Ross!
Übrigens: In der heutigen Zeit kann es schwierig werden, die
Abschlussmeldung einer Konsolenanwendung zu lesen.
Programmierer schrieb:> Natürlich wäre es besser hier direkt die C++-Klassen wie std::ifstream> zu nutzen statt das C-I/O über fopen() & Co. Im Idealfall hat man genau> eine Stelle mit Fehlerbehandlung, direkt in der main(), und der> restliche Code sieht so aus als gäbe es keine Fehler, indem man den> Compiler das Rückwärts-Abwickeln erledigen lässt.
Ja, Exceptions erleichtern einiges. Aber: viele Programmierer benutzen
sie falsch, vergessen insbesondere, nach oben die eigentliche Ursache
zurückzugeben, so dass am Ende für den Benutzer eine völlig nutzlose
Fehlermeldung rauskommt, die ihm keinerlei Anhaltspunkt mehr dazu
liefert, was eigentlich das Problem war.
Der Extremfall, gerade beim Zugriff auf Dateien, sind da wohl die
Winzigweich-Programmierer. Im Zweifel wird immer irgendwelcher Bullshit
mit fehlenden Rechten gemeldet. Wohl weil mal irgensoein Blinder Wichser
festgestellt hat, dass die meisten Fehler beim Dateizugriff genau darauf
zurückgehen. Ja, das wird vermutlich sogar stimmen, aber leitet den User
völlig in die Irre, wenn's mal nicht daran liegt...
c-hater schrieb:> Ja, Exceptions erleichtern einiges. Aber: viele Programmierer benutzen> sie falsch,
Ja das stimmt leider.
c-hater schrieb:> vergessen insbesondere, nach oben die eigentliche Ursache> zurückzugeben,
Viel schlimmer ist das wilde Mischen mit Rückgabecodes und Mengen an
try-catch-Blöcken; im Idealfall gibt es davon nur sehr wenige. z.B. nur
einen in der main() bei einfachen Konsolen-Programmen, oder einen pro
Menüpunkt in einer GUI-Anwendung, welcher dann eine Fehlermeldung
ausgibt. Leider wird korrektes Exception-Handing bzw. allgemein
Fehler-Behandlung in vielen Büchern nicht gut erklärt, ist aber IMO
ziemlich essentiell. In C++ ist das ja auch eng mit RAII (bzw. OOP)
verknüpft. Das lässt sich hier auch nicht mal eben schnell in einem
Beitrag erläutern...
c-hater schrieb:> so dass am Ende für den Benutzer eine völlig nutzlose> Fehlermeldung rauskommt, die ihm keinerlei Anhaltspunkt mehr dazu> liefert, was eigentlich das Problem war.
In Java sind dafür Chained Exceptions beliebt; man packt z.B. die
FileNotFoundException in eine eigene Exception namens
LogFileNotFoundException sodass sich dann ein strukturierter Backtrace
ergibt. Leider wird das dann bei der Ausgabe häufig ein ziemlicher
Datenwust.
c-hater schrieb:> m Zweifel wird immer irgendwelcher Bullshit> mit fehlenden Rechten gemeldet.
Das kann Android auch! Da ist's immer "Permission Denied". Ob jetzt die
DAC-Rechte oder die SELinux-Konfiguration (und welche der tausenden
dafür zuständigen Zeilen) oder die diversen Prozess-Capabilities schuld
sind darf man erraten.
Programmierer schrieb:> In Java sind dafür Chained Exceptions beliebt; man packt z.B. die> FileNotFoundException in eine eigene Exception namens> LogFileNotFoundException sodass sich dann ein strukturierter Backtrace> ergibt. Leider wird das dann bei der Ausgabe häufig ein ziemlicher> Datenwust.
So what? Lieber einen Datenwust von einer Fehlermeldung, der aber
immerhin einen Anhaltspunkt für die Wurzel dieses Wusts gibt, als eine
"irgendwas ist schief gegangen"-Summary, ggf. noch garniert mit einem
default, der im konkreten Fall möglicherweise garnicht zutrifft. Was ist
wohl nützlicher?
Übrigens sind diese Chains natürlich prinzipiell in jeder Sprache
möglich, die nested Exceptions erlaubt, nicht nur in Java.