Forum: Mikrocontroller und Digitale Elektronik Rückgabewerte von Funktionen beim Erfolg / Fehler


von zitter_ned_aso (Gast)


Lesenswert?

Hallo,

gibt es so etwas wie eine Vorschrift oder eine Regel für bestimmte 
Rückgabewerte von Funktionen bei einer erfolgreichen / fehlerhaften 
Ausführung?

Die main-Methode liefert ja im Erfolgsfall eine 0 (EXIT_SUCCESS) zurück 
(und eine 1 (EXIT_FAILURE) falls etwas schief läuft). Das braucht wohl 
das Betriebssystem.

Und diese Festlegung (return 0 == keine Fehler und return 1 == Fehler) 
wird anscheinend auch bei anderen Funktionen benutzt.
1
if(rename(oldname, newname)!=0){
2
 //error
3
}
Aber warum? Die if-Bedingung würde doch besser und logischer aussehen, 
wenn man diese Zahlen vertauscht (return 0 = keine Fehler, return 1 == 
Fehler).
1
if(!rename(oldname, newname)){
2
 //error
3
}
4
5
if(!delete(my_file)){
6
 //error
7
}
Also quasi die Rückgabewerte als bool-Werte betrachten (1 == true == 
alles in Ordnung, 0 == false == Fehler).

von zitter_ned_aso (Gast)


Lesenswert?

zitter_ned_aso schrieb:
> Die if-Bedingung würde doch besser und logischer aussehen,
> wenn man diese Zahlen vertauscht (return 0 = keine Fehler, return 1 ==
> Fehler).

Korrektur:

return 0; ----> Fehler
return 1; ----> kein Fehler

von Timo N. (tnn85)


Lesenswert?

Hab ich mich auch schon gefragt ob es da eine Konvention gibt.

Allerdings kenne ich auch negative Rückgabewerte für Fehler (int) und 
über den Wert (z.b -1; -2; -3;....) wird auch gleich die Fehlerursache 
mitgeliefert.

von zitter_ned_aso (Gast)


Lesenswert?

Oje, ich habe meine Frage im falschen Unterforum gepostet. Sorry, das 
habe ich ganz übersehen.

von Theor (Gast)


Lesenswert?

Eine nicht uninteressante Frage, auf die ich auf die Schnelle auch keine 
Antwort im Netz finden konnte, Vermutlich wird man aber beim Studium 
sehr früher OS Dokumentationen Unix, BSD, CP/M etc. möglicherweise auf 
eine Erklärung stossen.

Bis dahin würde ich mir das so erklären:

Angenommen, es wäre so, wie Du vorschlägst. Also:

> return 0; ----> Fehler
> return 1; ----> kein Fehler

und ich will die Fehler detailliert unterscheiden können, dann ergäbe 
sich zusätzlich (das sind jetzt nur so Hausnummern):

2 --- falsche Eingabe
3 --- Underflow
4 --- Out of memory
etcpp.

also folgende Bereiche:
1 ist kein Fehler
aber 0 und {2, 3, ..., n} ---> Fehler

Der Test
1
if (!errorcode) { ... }

würde also nicht immer dann wahr sein, falls kein Fehler aufgetreten 
ist.

Man könnte noch fragen, ob es nicht anders herum, mehrere Möglichkeiten 
des Erfolgs gibt, aber das kompliziert die Sache nur (je nachdem man das 
für kompliziert hält), weil man dann das Unterschreiten (oder 
Überschreiten) einer Schwelle testen müsste.

So also kann ich mit
1
if (!errorcode) { ... }

falls, das Ergebnis wahr ist, grundsätzlich davon ausgehen das alles 
OK ist. Andernfall muss (oder kann ich wenigsten) die Fehler 
differenzieren; was ich, wie ich oben unterstellte, bei Erfolg nicht tun 
will. Es gibt vielleicht in einigen Fällen mehrere Möglichkeiten des 
Erfolgs, aber die werden dann einfach erledigt (die Erfolge) und ich 
will hinterher garnicht wissen, was genau den Erfolg ausgemacht wird 
bzw. ich erkenne das an ganz anderen Dingen (Datei wurde erzeugt, Inhalt 
ist wie erwartet, Gerät tut was ich will).

Das ist nur eine Erklärung der bestehenden Situation, keine darüber, was 
sich die ersten Entwickler so dabei gedacht haben, Aber ich vermute, sie 
wird ziemlich gut passen.

von Maxe (Gast)


Lesenswert?

Man kann so halt mehrere verschiedene Fehlercodes zurueckgeben. Gibt 
eigentlich nicht besonders viel dran zu verstehen?

von Theor (Gast)


Lesenswert?

Ein Hinweis noch. Es gibt tatsächlich Systeme, die das etwas anders 
machen und auch Erfolge differenzieren (können): Etwa 
https://en.wikipedia.org/wiki/HRESULT

von zitter_ned_aso (Gast)


Lesenswert?

Ja, das ist ein Argument.

Bei mehreren Fehlercodes reicht die Null nicht aus.

von S. R. (svenska)


Lesenswert?

Theor schrieb:
> Ein Hinweis noch. Es gibt tatsächlich Systeme, die das etwas
> anders machen und auch Erfolge differenzieren (können)

Dann gibt es noch so Systeme, die unspezifisch "Fehler" zurückgeben und 
dann erwarten, dass man eine bestimmte Funktion (z.B. GetLastError, 
SDL_GetError) aufruft oder eine bestimmte Variable (errno) untersucht, 
um die genaue Ursache herauszufinden. Solche Systeme sind unpraktisch, 
weil sie ohne weiteres nicht thread-safe sind.

Und dann gibt es noch Verfahren, die nicht zuverlässig zwischen Erfolgen 
und Fehlern unterscheiden können, z.B. liefert atoi() im Fehlerfall -1 
zurück, was aber auch einen Erfolg kennzeichnen kann. Solche Systeme 
sind große Scheiße.

In moderneren Programmiersprachen gibt es häufig die Möglichkeit 
mehrerer Rückgabewerte und dort bekommt man dann oft einen Vektor 
[Status, Rückgabewert] zurück.

Aber in C ist "0 ist Erfolg" eine weit verbreitete Konvention, gerade 
weil sie verschiedene Fehlerfälle ohne Aufwand unterscheiden kann.

von A. S. (Gast)


Lesenswert?

Wenn Du keinen Rückgabewert brauchst, ist bool gut (0 ist schlecht, 
alles andere gut)

Wenn Du echte, positive Rückgabewerte brauchst, ist 0, <0 oder <=0 als 
Fehler gut.

Bei Pointern ist es meistens 0=Fehler.

Wenn Du keinen Rückgabewert brauchst, aber dem Benutzer Fehlerhinweise 
geben willst, dann ist 0 für OK gut: es gibt nicht DEN Grund, warum 
etwas funktioniert, aber einen ersten (oder wichtigsten), warum etwas 
nicht funktioniert.

Für embedded sollte man m.E. nur OK/nicht OK zurück liefern, wenn es 
keinen Benutzer gibt, der direkt eingreift. Die Fehlerinfos gehören 
stattdessen in irgendeiner Weise geloggt, gezählt, gespeichert. Sonst 
wird jeder Aufruf (Einzeiler) von 30 Zeilen was ist wenn zugemüllt.

von Wolfgang (Gast)


Lesenswert?

A. S. schrieb:
> Die Fehlerinfos gehören
> stattdessen in irgendeiner Weise geloggt, gezählt, gespeichert. Sonst
> wird jeder Aufruf (Einzeiler) von 30 Zeilen was ist wenn zugemüllt.

Warum?

Fehlerfrei = 0

Fehler <> 0, ob der Grund für den Fehler interessiert und dekodiert 
werden muss, hängt vom Aufrufer ab. Die "30 Zeilen" sind also nur 
notwendig, wenn die Fehlerursache ermittelt werden muss und können auch 
in eine Funktion gepackt werden.

von Wolfgang (Gast)


Lesenswert?

p.s.
In C wird bei logischen Entscheidungen zwischen =0 und <>0 getrennt, 
nicht zwischen =0 und =1.

Beitrag #6170511 wurde von einem Moderator gelöscht.
von Dergute W. (derguteweka)


Lesenswert?

Moin,

Jetzt hab' ich natuerlich nicht in alle diese Files reingeguckt, geh' 
aber mal stark davon aus, dass die alle voll bis unters Dach sind mit 
faulen Ausreden warum irgendwas (mutmasslich ein Funktionsaufruf) nicht 
funktioniert hat...
Wenn was funktioniert hat, interessierts erfahrungsgemaess keine Sau, 
warum. Es hat ja funktioniert.
1
wk [ ~ ]$ find /usr/include/ -name "*errno*"
2
/usr/include/apr-1/apu_errno.h
3
/usr/include/apr-1/apr_errno.h
4
/usr/include/octave-4.4.1/octave/oct-errno.h
5
/usr/include/bits/errno.h
6
/usr/include/CLucene/ext/boost/exception/errinfo_errno.hpp
7
/usr/include/boost/cerrno.hpp
8
/usr/include/boost/compatibility/cpp_c_headers/cerrno
9
/usr/include/boost/geometry/srs/projections/impl/pj_strerrno.hpp
10
/usr/include/boost/exception/errinfo_errno.hpp
11
/usr/include/gsl/gsl_errno.h
12
/usr/include/ghostscript/errno_.h
13
/usr/include/uv/errno.h
14
/usr/include/schily/errno.h
15
/usr/include/asm-generic/errno-base.h
16
/usr/include/asm-generic/errno.h
17
/usr/include/node/uv-errno.h
18
/usr/include/linux/errno.h
19
/usr/include/sys/errno.h
20
/usr/include/c++/8.2.0/cerrno
21
/usr/include/errno.h
22
/usr/include/asm/errno.h
23
/usr/include/libnl3/netlink/errno.h
24
wk [ ~ ]$

Gruss
WK

von Peter D. (peda)


Lesenswert?

Für Funktionen, die wirklich nur 2 Werte zurückliefern, nehme ich bool 
als Typ. Und dann aber auch true für o.k. und false für false.

von A. S. (Gast)


Lesenswert?

Wolfgang schrieb:
> Die "30 Zeilen" sind also nur
> notwendig, wenn die Fehlerursache ermittelt werden muss und können auch
> in eine Funktion gepackt werden.

Merkst Du?

von Koko (Gast)


Lesenswert?

Da gibt es keine GESETZE.

Aber sinnvolle und weniger schlaue Verhaltensregeln:

Bei kleinen Aufgaben ist der Rückgabewert direkt brauchbar:
- Welche Taste wurde gedrückt?
- Wieviele ms hat es gedauert?
- Der Rückgabewert Null = 0 wäre dann kein Fehler-Code, sondern
  hieße nur: Da kam nix, da war nix...

Bei komplexeren Funktionen ist eher interessant, ob sie
fehlerfrei abliefen (Error = 0), oder weshalb es nicht geklappt
hat.

ERROR = 0 = OK = OHNE PROBLEM ist sehr sinnvoll:
Wenn es gut gelaufen ist, will keiner wissen, warum!

ERROR != 0, (oder <> 0,) bedeutet: Da liegt ein Problem vor.
- Und wenn der Programmierer sich Mühe gegeben hat, ist der
Error-Code vielleicht eine Hilfe zur künftigen Fehlervermeidung!

von Bauform B. (bauformb)


Angehängte Dateien:

Lesenswert?

zitter_ned_aso schrieb:
> gibt es so etwas wie eine Vorschrift oder eine Regel für bestimmte
> Rückgabewerte von Funktionen bei einer erfolgreichen / fehlerhaften
> Ausführung?

So um 1975 herum hat Digital Equipment festgelegt, wie Rückgabewerte 
auszusehen haben (für VMS). Weil früher alles viel besser war, ist die 
Vorschrift ein wenig umfangreicher geworden, 6 Seiten im Handbuch¹ (bzw. 
35, wenn man Exceptions dazu zählt). Die Vorschrift gilt heute noch und 
wurde von Microsoft für NT leicht modifiziert übernommen:

Theor schrieb:
> Ein Hinweis noch. Es gibt tatsächlich Systeme, die das etwas anders
> machen und auch Erfolge differenzieren (können): Etwa
> https://en.wikipedia.org/wiki/HRESULT


1) 
https://support.hpe.com/hpesc/public/docDisplay?docId=emr_na-c04621389

von FS (Gast)


Lesenswert?

Eine Vorschrift oder gar Standard gibt es meines Wissens nicht. Das 
kommt immer darauf an, wie eine API/Library das per Konvention umsetzt. 
Da C keine Ausnahmen oder ähnliches kennt, behilft man sich oft mit 
Rückgabecodes. Bei Vulkan z.B. ist es VkResult (eine simple Enumeration) 
oder bei Microsofts COM das HRESULT (ein strukturierter 32-bit 
Ganzzahl-Fehlercode). Das Prinzip ist i.d.R. immer das gleiche: Ein Wert 
von 0 zeigt den Erfolg an, jeder andere Wert einen Fehler, den man 
entweder als positiven oder negativen Integerwert ablesen kann. Einige 
alte Windows APIs geben auch "bool" (hier ein 32-bit Integer) zurück, 
tritt dort ein Fehler auf kann man dann mit GetLastError() den damit 
verbundenen Fehlercode für den aktuellen Thread abrufen. Warum "0" als 
Erfolgsmeldung? Weil es die Bedingung einfacher macht. Man muss so nur 
auf "0" prüfen, ob etwas schief gelaufen ist, völlig egal was der Fehler 
war.

Da so eine Funktion nicht direkt einen Wert zurückgeben kann, werden 
diese meist als letzter Paramter mit einem Pointer-to-Pointer übergeben.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Die klassischen Unix System-APIs verwenden folgende Konvention:

0: Erfolgreich
-1: Fehler

Für Calls die einen Wert zurückgeben:

>= 0: Gültiger Wert
-1: Fehler

Der eigentliche Fehlercode steht in errno.

In seltenen Fällen, wenn auch negative Werte gültige Rückgabewerte sind, 
zählt nur errno. Dabei muss der Programmierer errno vor dem 
Funktionsaufruf selber zurück setzen:
1
errno = 0;
2
result = systemcall();
3
if(errno) {
4
    perror(NULL);
5
    exit(EXIT_FAILURE);
6
}

Die klassischen Unix Library-APIs halten sich meist an eine ähnliche 
Konvention:

-1 oder NULL-Pointer: Fehler
>=0 oder !NULL: Erfolgreich

Da mittlerweile eine ganze Reihe von Bastlern an den Linux-Libraries 
tätig waren löst sich diese Konvention langsam auf.

von Koko (Gast)


Lesenswert?

> So um 1975 herum hat Digital Equipment festgelegt ...
Very interesting, but 45 years ago...

>  zitter_ned_aso (Gast)
wollte aber wohl eher wissen, wie sie/er da selbst
erfolgsversprechend rangeht.

Da ist weniger ein 35-seitiges Pamphlet für Juristen gefragt,
sondern eher eine grundsätzlich einleuchtende Vorgehensweise.

Sinnfällige (!) Regeln sind erfahrungsgemäß für logisch denkende
IT-ler und Techniker leichter einzuhalten, als sich das logik-
ferne Fakultäten vorstellen können.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.