Wie Programm beenden?

Gast #6179947
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?
Gast #6179966
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.
#6179967
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.
#6179971
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.
Gast #6179982
1
    #include <stdlib.h>
2
    #include <string.h>
3
    ...
4
    FILE *fp;
5
    ...
6
    fp = fopen(filename, "r");
7
    if (fp == NULL)
8
    {
9
        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.
Gast #6180004
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
void foo () {
5
  FILE* f = fopen ("test.txt", "r");
6
  if (!f)
7
    throw std::runtime_error ("Datei konnte nicht geöffnet werden");
8
  
9
  puts ("Mach was mit der Datei");
10
  fclose (f);
11
}
12

13
void bar () {
14
  puts ("bar(). Mache was...");
15
  foo ();
16
  puts ("bar(). Mache nochwas mehr...");
17
}
18

19
int main () {
20
  try {
21
    puts ("main(). Mache was...");
22
    bar ();
23
    puts ("main(). Mache nochwas mehr...");
24
    return 0;
25
  } catch (const std::exception& e) {
26
    fprintf (stderr, "Fehler: %s\n", e.what ());
27
    return 1;
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.
#6180051
@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.
Gast #6180063
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...
Gast #6180077
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.
Gast #6180416
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.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 6 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.