Hallo zusammen, ich bin mal wieder bei einer Null-Frage (d.h. eine Frage, die ich schon vor langer, langer Zeit hätte klären sollen). Sehr viele Funktionen in C geben einfach nur einen Fehlercode zurück. Traditionell haben diese Funktionen den Datentyp "int" und Fehlercodes ("EXIT_SUCCESS" u.Ä.) sind als "defines" in einem Header (stdlib.h) hinterlegt. Auf dem AVR bin ich mir sehr sicher, niemals mehr als 8 Bit für einen Fehlercode zu benötigen, also ist der Rückgabewert für diese Funktionen bei mir plattformübergreifend "int_fast8_t". Andererseits könnte ich mir auch ein enum mit den möglichen Fehlercodes definieren, mit dem Flag "-fshort-enums" kompilieren und daraus einen Datentyp "return_t" definieren. Oder. Welchen Datentyp nutzt man heutzutage für die Rückgabe von Fehlercodes? Viele Grüße W.T.
Walter T. schrieb: > Sehr viele Funktionen in C geben einfach nur einen Fehlercode zurück. > Traditionell haben diese Funktionen den Datentyp "int" Das liegt in der Geschichte. int war von Anfang an in C als der 'Leib- und Magen' Datentyp für eine bstimmte Maschine gedacht. D.h. der Datentyp mit dem eine CPU normalerweise am schnellsten und einfachsten hantieren kann (und der noch über einen genügend grossen Zahlenraum verfügt, so dass man auch etwas damit machen kann). Das traf auf viele Workstations und größere Rechner zu. Nur gab es zur damaligen Zeit noch keine Mikrocontroller bzw. die 8 Bit Szene lag mehr oder weniger in den Kinderschuhen. Niemand konnte sich damals vorstellen, dass es möglich wäre einen kompletten Computer in weniger als 2 Kubikmeter Gehäuse unterzubringen. > Welchen Datentyp nutzt man heutzutage für die Rückgabe von Fehlercodes? Auf einem AVR würde ich zu uint8_t greifen. So wie für fast alles, was im Wertebereich nicht explizit einen 16 Bit int erfordert.
:
Bearbeitet durch User
Karl H. schrieb: > Auf einem AVR würde ich zu uint8_t greifen. So wie für fast alles, was > im Wertebereich nicht explizit einen 16 Bit int erfordert. Hallo Karl-Heinz, danke für Deine Ausführungen. Das heißt für mich: Wenn ich ein wenig plattformunabhängiger bleiben will, ist uint_fast8_t der richtige Datentyp. Vielleicht sollte ich mir einfach ein kurz-typedef ausdenken - der Typname ist so lang, daß es einfach irgendwie falsch aussieht... Hat es einen bestimmten Grund, daß Du uint8_t und nicht int8_t nutzt? VerODERst Du einzelne Fehlerzustände im Rückgabewert?
Wie Karl Heinz gesagt hat, wurde ursprünglich der Wert, der der natürlichen Datenbreite des jeweiligen Prozessors entspricht, als Standartrückgabewert definiert. Dieses Konzept ist aber m.E. nicht mehr unbedingt zwingend. Die heutigen Compiler machen daraus ein: "Ein Wert wird zurückgegeben". Je nach Implementation bedeutet dies, dass Du ein Word ein Longword oder einen Zeiger (meist mehr als 8 Bit) zurückgeben kannst. Der Compiler verteilt dies auf mehrere Register und setzt sie bei der Rückgabe auch wieder zusammen. "Ein Wert wird zurückgegeben" bedeutet in diesem Zusammenhang: Es können z.B. 8 Bit char, unsigned char, word oder auch Pointer (oft 4 Bytes) zurückgeliefert werden auch der Buchstabe 'A' (char) sowohl in der 8-Bit Version als auch in der mit 16-Bit (wide char), aber 2 Zeichen wie 'A' + 'B' oder "AB" gehen nicht. Auch wenn sie in zwei Bytes oder bei einem 16 Bit Prozessor in ein Register passen würden.
Walter T. schrieb: > Hat es einen bestimmten Grund, daß Du uint8_t und nicht int8_t nutzt? Ein Returnwert kleiner als int wird ab und zu vom Compiler zu int erweitert. Das ist bei uint8_t oft billiger als bei int8_t.
Da der Return-Wert eh nur temporär auf dem Stack landet, "kostet" er ja nicht wirklich etwas (Speicherbedarf).
:
Bearbeitet durch User
umsomehr, als er üblicherweise nicht mal auf dem Stack zurückgegeben wird, sondern in einem Register (solange es ein einfacher Typ wie int oder Zeiger ist und keine struct).
Walter T. schrieb: > Das heißt für mich: Wenn ich ein wenig > plattformunabhängiger bleiben will, ist uint_fast8_t der richtige > Datentyp. Ja, genau diesen Datentyp benutze ich in µC-Familien-übergreifender Software am liebsten, wenn der Wertebereich 0...255 vollkommen ausreicht.
Walter T. schrieb: > Karl H. schrieb: >> Auf einem AVR würde ich zu uint8_t greifen. So wie für fast alles, was >> im Wertebereich nicht explizit einen 16 Bit int erfordert. > > Hallo Karl-Heinz, > danke für Deine Ausführungen. Das heißt für mich: Wenn ich ein wenig > plattformunabhängiger bleiben will, ist uint_fast8_t der richtige > Datentyp. Vielleicht sollte ich mir einfach ein kurz-typedef ausdenken - > der Typname ist so lang, daß es einfach irgendwie falsch aussieht... Das typedef kann ich nur unterstützen. Einerseits kannst du dann einfacher den Datentyp wechseln wenn uint8_t (oder was auch immer) doch nicht ausreicht, andererseits ist ein Typ "error_code" quasi selbstsprechend.
Walter T. schrieb: > ich bin mal wieder bei einer Null-Frage (d.h. eine Frage, die ich schon > vor langer, langer Zeit hätte klären sollen). > > Sehr viele Funktionen in C geben einfach nur einen Fehlercode zurück. > Traditionell haben diese Funktionen den Datentyp "int" und Fehlercodes > ("EXIT_SUCCESS" u.Ä.) sind als "defines" in einem Header (stdlib.h) > hinterlegt. > > Auf dem AVR bin ich mir sehr sicher, niemals mehr als 8 Bit für einen > Fehlercode zu benötigen, also ist der Rückgabewert für diese Funktionen > bei mir plattformübergreifend "int_fast8_t". > > Andererseits könnte ich mir auch ein enum mit den möglichen Fehlercodes > definieren, mit dem Flag "-fshort-enums" kompilieren und daraus einen > Datentyp "return_t" definieren. > > Oder. > > Welchen Datentyp nutzt man heutzutage für die Rückgabe von Fehlercodes? Da kann man IMHO nur mit Radio Eriwan antworten: "kommt darauf an". Worauf kommt es an? Darauf, was mit dem Fehlercode / Rückgabewert gemacht werden soll. Ich hab' da mal was vorbereitet (in C99), siehe Anhang. Für die benötigte Funktion im Anhang reicht im Prinzip natürlich das aus, was divide0() macht: bei einer ZeroDivision gibt die Funktion den Wert 0.0 zurück, was im Kontext unmöglich ist und daher einen Fehler signalisiert. Sowas kann man machen, wenn man Fehler direkt nach dem Funktionsaufruf behandelt, sonst kann das sehr schnell häßlich werden. Was divide1() und divide2() machen, ist IMHO fast noch fieser; divide1() hat nichtmal einen sprechenden Bezeichner für den Fehler, divide2() hat zwar einen Bezeichner, aber beide Funktionen benötigen jeweils eigene und weitgehend funktionsspezifische Datenstrukturen für die Rückgabe, auf deren Elemente dann zugegriffen werden muß. Möglich, aber gruselig, und relativ aufwändig zu pflegen und erweitern! Die eleganteste, les-, auswert- und erweiterbarste Lösung ist IMHO die in divide3(): da wird eine Enum-Variable mit dem passenden Fehlercode gesetzt und kann an beliebiger Stelle ausgewertet werden. Die Enumeration kann da nicht nur den Fehlercode für eine, sondern auch die für andere Funktionen enthalten; die Fehlercodes können (bei entsprechender Deklaration) sogar verODERt werden. Und wenn eine Funktion nicht nur wie die hier auf einen einzigen Fehlercode ZeroDivision, sondern sehr viele verschiedene Fehler reagieren können soll, scheint mir die Auswertung mit switch() sehr viel flexibler und eleganter als alle anderen Möglichkeiten. Insofern gibt es IMHO keinen Königsweg. Ich persönlich neige zu Lösungen wie in divide3(), finde globale Variablen aber unelegant und neige daher meistens dazu, die errno-Enum static in eine eigene Header-Datei zu packen und nur über definierte Funktionen darauf zuzugreifen, und dann kann man natürlich auch gleich etwas wie strerror(3) mit den einzelnen Meldungen implementieren. Das ist dann die hübscheste Lösung für alles.
Sheeva P. schrieb: > Für die benötigte Funktion im Anhang reicht im Prinzip natürlich das > aus, was divide0() macht: bei einer ZeroDivision gibt die Funktion den > Wert 0.0 zurück, was im Kontext unmöglich ist und daher einen Fehler > signalisiert. Warum sollte das unmöglich sein? 0.0/5.0 ist doch 0.0, oder nicht? Du könntest besser INFINITY oder NAN aus <math.h> zurückliefern.
Rolf M. schrieb: > Sheeva P. schrieb: >> Für die benötigte Funktion im Anhang reicht im Prinzip natürlich das >> aus, was divide0() macht: bei einer ZeroDivision gibt die Funktion den >> Wert 0.0 zurück, was im Kontext unmöglich ist und daher einen Fehler >> signalisiert. > > Warum sollte das unmöglich sein? 0.0/5.0 ist doch 0.0, oder nicht? Du > könntest besser INFINITY oder NAN aus <math.h> zurückliefern. Klar. Aber darum ging es ja nicht. <:)
Rolf M. schrieb: > Warum sollte das unmöglich sein? 0.0/5.0 ist doch 0.0, oder nicht? Das hat aber jemand die Division durch Null verstanden.... > Du > könntest besser INFINITY oder NAN aus <math.h> zurückliefern. DAS wiederrum ist korrekt.
:
Bearbeitet durch User
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.