Forum: Compiler & IDEs AVR-GCC/ARM-GCC: Datentyp für return


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,
ich habe (mal wieder) eine vermutlich total einfache Frage zu gutem 
C-Stil: Welches ist eigentlich der richtige Datentyp für Rückgabewerte, 
wenn diese nur Fehlerzustände signalisieren sollen (also normalerweise 
0)?

Beispiel wie es tausendfach vorkommt:
1
uint8_t functionThatMayFail(void) {
2
  // do something
3
  if( alles in ordnung) {
4
    // ...
5
    return EXIT_SUCCESS;
6
  }
7
  else {
8
    return EXIT_FAILURE; 
9
  }    
10
}
Wie man sieht, nutze ich aus AVR-Gewohnheiten uint8_t, weil der fuer 
alle Fehlerzustände ausreicht (wer will schon mehr als 255 
Fehlermeldungen in einer Funktion schreiben?). Die meisten 
Standardfunktionen nutzen wohl int, desgleichen in meinen C-Büchern und 
ich vermute, das geschieht aus traditionellen Gründen.

Welchen Datentyp nutzt man denn für guten Stil? Gibt es da etwas 
Vordefiniertes? Es böte sich ja ein enum-Typ an.

Viele Grüße
W.T.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter Tarpan schrieb:
> Welchen Datentyp nutzt man denn für guten Stil?

bool?

(#include <stdbool.h>, damit du ihn auch bei C bekommst, nicht nur
bei C++.)

von Frank K. (fchk)


Lesenswert?

Walter Tarpan schrieb:

> Wie man sieht, nutze ich aus AVR-Gewohnheiten uint8_t, weil der fuer
> alle Fehlerzustände ausreicht (wer will schon mehr als 255
> Fehlermeldungen in einer Funktion schreiben?). Die meisten
> Standardfunktionen nutzen wohl int, desgleichen in meinen C-Büchern und
> ich vermute, das geschieht aus traditionellen Gründen.

Nicht nur.
int kann von einem Compiler immer effizient verarbeitet werden. char 
oder Dein uint8_t (unsigned char) eben nicht überall. Bei x86 kannst Du 
direkt mit 8 Bit Registern arbeiten, ARM oder MIPS können das nicht. Da 
gibt es kein add al,bl und add ax,bx und add eax,ebx, sondern nur ein 
add r1,r2; wobei r1 und r2 immer ganze Register sind. Wenn dann als 
Ergebnis explizit ein 8 Bit Wert gefordert ist, brauchst Du noch das 
Äquivalent zu einem  and r1,#255 hinterher.

Daher eben int.

> Welchen Datentyp nutzt man denn für guten Stil? Gibt es da etwas
> Vordefiniertes? Es böte sich ja ein enum-Typ an.

... der intern wieder ein int ist.

Wenn Du nur Wahrheitswerte zurückgeben willst, dann mach es doch. bool 
existiert (mit stdbool.h), und dann weiß jeder, dass er nur ein true 
oder false zu erwarten hat.

fchk

von Peter D. (peda)


Lesenswert?

fastest unsigned int with at least 8 bits:

uint_fast8_t

von Rolf Magnus (Gast)


Lesenswert?

Frank K. schrieb:
> int kann von einem Compiler immer effizient verarbeitet werden.

Solange man nicht auf einem 8-Bit-Prozessor arbeitet. Dort ist int für 
den Compiler eher umständlich zu handhaben.

Frank K. schrieb:
> Da gibt es kein add al,bl und add ax,bx und add eax,ebx, sondern nur ein
> add r1,r2; wobei r1 und r2 immer ganze Register sind. Wenn dann als
> Ergebnis explizit ein 8 Bit Wert gefordert ist, brauchst Du noch das
> Äquivalent zu einem  and r1,#255 hinterher.

Nun rechnet man aber mit Fehlercodes in der Regel nicht, sondern setzt 
sie nur und vergleicht sie. Dazu muß nirgends irgendein Ergebnis auf 8 
Bit beschnitten werden.

von Walter T. (nicolas)


Lesenswert?

Danke für die Antworten.

Schade, ich hatte gehofft, daß die Antwort in etwa lautet wie: 
"Mööönsch, genau dafür gibt es doch 'return_t', was hast Du für 
schlechte Bücher?"

Aber offensichtlich gehen da sogar die Meinungen der Experten 
auseinander.

Peter Dannegger schrieb:
> uint_fast8_t


Jörg Wunsch schrieb:
> bool?

Irgendetwas, was zu uint_fast8_t ausgedrückt wird war mir auch 
vorgeschwebt. Bool ist mir doch etwas zu wenig. Zumindest meine 
Test-Routinen werten die Rückgabewerte etwas detaillierter aus, um 
sinnvolle Fehlermeldungen zu werfen.

Rolf Magnus schrieb:
> Nun rechnet man aber mit Fehlercodes in der Regel nicht, sondern setzt
> sie nur und vergleicht sie. Dazu muß nirgends irgendein Ergebnis auf 8
> Bit beschnitten werden.

Also ich gehöre auch zu den Leuten, denen die Rechenaufwand-Effizienz 
bei Fehlermeldungen total egal ist - zumindest sobald sie ungleich Null 
sind. Und in diesem Fall ist mir wichtiger, eine möglichst genaue 
Meldung zu bekommen, da das System danach ohnehin in einem sicheren 
Zustand sein Dasein in einer Endlosschleife fristet. Für "gracefull 
degradation" sind meine Anwendungen aber auch noch zu einfach.

Aber Fehlerbehandlung ist für mich eh noch etwas Rätselhaftes. Alle 
C-Bücher und Tutorials und Application Notes, die ich mir bislang 
durchgearbeitet habe beschränken sich darauf, für die Fehlerbehandlung 
etwas wie
1
if (error) {
2
 // Hier Fehlerbehandlung reintun
3
}
, wobei sich kaum jemand die Mühe macht zu beschreiben, wie eine 
sinnvolle Fehlerbehandlungsroutine überhaupt aussieht.

Danke für die Diskussion!
W.T.

von Frank K. (fchk)


Lesenswert?

Rolf Magnus schrieb:

>> int kann von einem Compiler immer effizient verarbeitet werden.
>
> Solange man nicht auf einem 8-Bit-Prozessor arbeitet. Dort ist int für
> den Compiler eher umständlich zu handhaben.

... was zeigt, dass C ursprünglich nicht auf einer 8 Bit Maschine 
entstanden ist. Du wirst im Standard viele Stellen finden, wo von dieser 
Annahme ausgegangen wird.

>> Da gibt es kein add al,bl und add ax,bx und add eax,ebx, sondern nur ein
>> add r1,r2; wobei r1 und r2 immer ganze Register sind. Wenn dann als
>> Ergebnis explizit ein 8 Bit Wert gefordert ist, brauchst Du noch das
>> Äquivalent zu einem  and r1,#255 hinterher.
>
> Nun rechnet man aber mit Fehlercodes in der Regel nicht, sondern setzt
> sie nur und vergleicht sie. Dazu muß nirgends irgendein Ergebnis auf 8
> Bit beschnitten werden.

Das weiß der Compiler aber nicht, und das steht auch nicht so im 
Standard.

fchk

von Dr. Sommer (Gast)


Lesenswert?

1
#include <iostream>
2
#include <stdexcept>
3
4
void functionThatMayFail(void) {
5
  // do something
6
  if( alles in ordnung) {
7
    // ...
8
    return;
9
  }
10
  else {
11
    throw std::logic_error ("Etwas ist schiefgelaufen");
12
  }    
13
}
14
15
int main () {
16
  try {
17
    functionThatMayFail ();
18
  } catch (std::exception& ex) {
19
    std::cout << ex.what () << '\n';
20
  }
21
}
Und mit "g++" statt "gcc" kompilieren. Allerdings nur zu empfehlen wenn 
viel RAM&Programmspeicher da ist und dynamische Speicherverwaltung.

von Rolf Magnus (Gast)


Lesenswert?

Walter Tarpan schrieb:
> Aber Fehlerbehandlung ist für mich eh noch etwas Rätselhaftes.

Entweder ein enum oder ein passender Integer-Typ und dann mit #define 
gegebene Rückgabecodes. Eine andere Methode, die auch von manchen APIs 
verwendet wird, ist eine Rückgabe, die nur sagt "fehler" oder "ok" und 
dann ein getError() oder so, das im Fehlerfall einen Fehlercode der 
letzen Aktion zurückgibt. In Standard-C gibt sowas - leider aber nicht 
als Funktion - ja auch, in Form von errno.

Frank K. schrieb:
> Rolf Magnus schrieb:
>
>>> int kann von einem Compiler immer effizient verarbeitet werden.
>>
>> Solange man nicht auf einem 8-Bit-Prozessor arbeitet. Dort ist int für
>> den Compiler eher umständlich zu handhaben.
>
> ... was zeigt, dass C ursprünglich nicht auf einer 8 Bit Maschine
> entstanden ist. Du wirst im Standard viele Stellen finden, wo von dieser
> Annahme ausgegangen wird.

Natürlich. Aber eine der Zielplattformen in diesem Thread ist AVR. Da 
hilfts nichts, daß auf den Plattformen, für die C gedacht war, int 
keinen Nachteil hätte. Ich empfehle auch nicht einen Porsche zum Ziehen 
eines Pflugs, nur weil der auf der Straße viel schneller als ein Traktor 
wäre.

>> Nun rechnet man aber mit Fehlercodes in der Regel nicht, sondern setzt
>> sie nur und vergleicht sie. Dazu muß nirgends irgendein Ergebnis auf 8
>> Bit beschnitten werden.
>
> Das weiß der Compiler aber nicht, und das steht auch nicht so im
> Standard.

Natürlich weiß er, daß er nicht gerechnet hat. Oder denkst du, der 
schneidet einfach vorsichtshalber regelmäßig immer wieder mal die oberen 
Bits ab, nur um sicher zu sein, daß nicht eins versehentlich umgekippt 
ist?
Bei einem
1
uint8_t add(uint8_t a, uint8_t b)
2
{
3
    return a + b;
4
}
muß er natürlich das Ergebnis beschneiden, aber bei
1
uint8_t machwas(void)
2
{
3
    if (fehler)
4
        return 3;
5
    else
6
        return 0;
7
}
muß er das nicht tun, denn er kann sich einigermaßen sicher sein, daß 
die Bits außerhalb des Bereichs von uint8_t schon alle 0 sind.

Dr. Sommer schrieb:
> Und mit "g++" statt "gcc" kompilieren. Allerdings nur zu empfehlen wenn
> viel RAM&Programmspeicher da ist und dynamische Speicherverwaltung.

Und wenn Exception Handling auf der Zielplattform überhaupt tut.

von Dr. Sommer (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Und wenn Exception Handling auf der Zielplattform überhaupt tut.
Mit ARM-GCC (siehe Topic-Titel) - JA.

von Rolf Magnus (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Rolf Magnus schrieb:
>> Und wenn Exception Handling auf der Zielplattform überhaupt tut.
> Mit ARM-GCC (siehe Topic-Titel) - JA.

Aber mit AVR-GCC, der ebenfalls im Titel steht, nicht.

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.