Forum: PC-Programmierung C: Funktionen und Rückgabewerte


von Ralf A. (warpnine)


Lesenswert?

Hi,

ich bin gerade dabei mir ein Schema zu überlegen, wie ich Rückgabewerte
von Funktionen handhaben soll. Gemeint sind Rückgabewerte, die Aussage
über evtl. aufgetretene Fehler geben.

Ich habe mir folgende Möglichkeiten überlegt:

1. Rückgabewerte sind fest in den Funktionen hinterlegt, also z.B. 0
für keinen Fehler, 1 für Fehler Nr.1, usw.
Das prüfen dieser Fehler kann jetzt auf zwei Arten geschehen:
Entweder man prüft auch direkt auf die entsprechenden Rückgabewerte,
oder indirekt, indem man zum besseren Verständnis den festen
Rückgabewerten per #define aussagekräftige Namen verpasst, und damit
dann den Rückgabewert prüft.

2. Die aus Punkt 1 beschriebenen per #define angelegten Fehler-Namen
werden auch in den Funktionen verwendet.
Das wiederum macht die Funktionen besser lesbar.
Allerdings stellt sich dann die Frage, wie es gehandhabt werden soll,
wenn z.B. zwei Funktionen einen gleichen Fehlernamen verwenden, obwohl
die Funktionen nichts miteinander zu tun haben.
Ein zentrales H-File mit allen Fehlern halte ich für unklug, zumal in
Betracht gezogen werden soll, dass auch Bibliotheken programmiert
werden sollen. Das heisst, bei einer Änderung einer bereits bestehenden
Zuweisung müssen alle Bibliotheken neu übersetzt werden.
Es sei denn, man kann sicherstellen, dass Bestehendes nie geändert
wird.

3. ??????
Wie macht ihr das denn?

Ralf

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Punkt 2 ist eine verbreitete Vorgehensweise.
Die Zuordnung von Fehlernummern und deren Bedeutung wird als
unveränderlich angesehen. Wenn eine (neue) Funktion einen bislang
unbekannten Fehlercode liefern soll, muss die Fehlernummernliste
entsprechend erweitert werden, es darf aber keine "alte" Fehlernummer
inhaltlich geändert werden.

So machen es Betriebssystem-APIs; gewisse Fehlercodes sind sogar
betriebssystemübergreifend standardisiert.

Sieh Dir mal errno.h oder error.h Deines Compilers an.

Natürlich lassen sich nicht alle möglichen Fehler auf dieses feste
Fehlernummernmodell abbilden - in diesem Falle werden oft eigene
Fehlercodetabellen für Libraries verwendet.

Die Verwendung eines enums für Fehlercodes erscheint mir übrigens recht
vorteilhaft, da a) moderne Debugger dann den symbolischen Namen anzeigen
können (was mit #defines nicht geht) und b) man sich nicht um die
tatsächlichen numerischen Werte Gedanken machen muss, solange man neue
Elemente immer "hinten" anhängt.

von Stefan (Gast)


Lesenswert?

Es gibt noch eine 3. Methode:

Eine Extrafunktion zum Abfragen, welcher Fehler in der letzten Funktion
aufgetreten ist.

Das hat den Vorteil, dass du in der zu prüfenden Funktion nur einen
Rückgabewert für den Fehlerfall verbrätst ("Es ist irgendein Fehler
aufgetreten") und die restlichen Rückgabewerte Nutzdaten sind.

Welcher Fehler genau aufgetreten ist, kann mit der Extrafunktion
geprüft werden.

von Nico S. (Gast)


Lesenswert?

Stichwort: errno

von Thomas (Gast)


Lesenswert?

Die dritte hier emfpohlene Methode bringt eigentlich keinen Vorteil. Was
soll es bringen, wenn der Returncode ohnehin für Fehlerinformation
verwendet wird, dann nur zwischen Fehler und kein Fehler zu
unterscheiden? Ausserdem erfordert er das Ablegen der Fehlerinformation
ausserhalb der Funktion - global? - Bäh! So was gibts leider in C und
auch Standardbibliotheken machen das, aber das muss man deswegen nicht
nachmachen ;)

"..., kann mit der Extrafunktion..." Genau, "kann", wird aber
nicht... Fehlermeldungen, die ignoriert werden können, sind keine
wirksamen Fehlermeldungen.

Zum zentralen Fehler-Headerfile. Hat seine Berechtigung und die
Wiederverwendung allgemeiner Fehler ist auch nicht schlecht. Bastelst
du unterschiedliche Bibliotheken mit eigenen Headers und jeweils
eigenen (aber gleichlautenden) Fehler-Defines, kommt es zu Problemen.
Da helfen enums oder namespaces (müsste es nach C99 mittlerweile auch
in C geben).

Ich würde eine "globale" Headerdatei für Fehler anfangen. Vermeide
auf jeden Fall die Verwendung von "magischen Zahlen" und arbeite
unbedingt mit symbolischen Namen, also enums oder halt defines!

Gruß

von A.K. (Gast)


Lesenswert?

"errno" als Beispiel für modulare Fehlercodes hinzustellen, ist schon
fast kriminell.

Besser: Fehlercodes nicht sequentiell numerieren, sondern Bereiche
definieren. Global wird nur der Bereich des jeweiligen Moduls
definiert, also beispielsweise:

<errors-global.h>
   #define ERR_MODULE_1   0x0100
   #define ERR_MODULE_2   0x0300
   #define ERR_MODULE_3   0x0200

<module-1.h>
   #define ERR_MOD1_WASSOLLICHDAMITANFANGEN (ERR_MODULE_1+0x01)
   usw.

C++ Namespaces sind auch ganz nett, lösen das Problem aber auch nicht
ganz, weil halt immer noch irgendwer global numerieren muss. Einzig
Strings vgl. Java-Classnames sind eindeutig, aber etwas unhandlich.

von Stefan (Gast)


Lesenswert?

Wer hat die 3. Methode empfohlen? Ich nicht.

Ich habe geschrieben, dass es diese Methode gibt und welchen Vorteil
ich darin sehe.

Wieso kann man nicht sachlich beim Thema bleiben und muss Leute als
kriminelle Ignoranten titulieren?

von A.K. (Gast)


Lesenswert?

Ok, sorry, war etwas zu agressiv formuliert. Erwähnt hat es übrigens
Rufus.

Weil ich weder Unix/Linux noch Windows als Vorbild für Modularisierung
und API-Design anraten möchte. Die errnos von Unix sind eine
Katastrophe, ähnlich schlimm wie Microsofts Namensgebung von APIs. Ich
hatte letzthin mal mit einem Backup-Programm zu tun, das einen
Schreibfehler per FTP lauthals auf ein verschmutztes Medium schob.
Typischer Fall von etwas zu schmalspurig konzipierter Fehlerhandhabung.

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.