Forum: Compiler & IDEs Enum als Rückgabewert - unschön?!


von Rainer K. (Gast)


Lesenswert?

Hallo Community,

ein Kommilitone sagte mir das ich unter keinen Umständen eine 
Enumeration als Rückgabewert verwenden soll und schon garnicht bei Code 
für embedded systeme.

Beispiel:
1
typedef enum {
2
        ROT = 1,
3
        BLAU,
4
        GELB,
5
        ORANGE
6
} FARBE;
7
8
FARBE exampleFnct (void) {
9
10
 return ROT;
11
}

Was genau ist daran so falsch? Er meinte ein guter Programmierer 
übergibt einen Pointer auf die Enum an die Funktion.

Ich könnte diese Aussage nachvollziehen wenn ein ENUM extrem groß wäre 
und bei einem Call-By-Value viele Daten um-kopiert werden müssten. 
Allerdings ist laut ISO/ANSI C ein ENUM maximal sizeof(int), also bei 
meinem System 32 Bit. Eben so groß ist ein Pointer auf eine Enum 
Variable. Es dürfte also keinen Performance unterschied machen.

Ich habe mir einige Coding Standards angeschaut... MISRA C und diverser 
andere... keiner weißt darauf hin dass ein ENUM nicht als return value 
verwendet werden sollte.

Ich programmiere schon seit einigen Jahren und hab mich deshalb über 
diese Aussage doch sehr gewundert... Ich wollte die Sache schon auf sich 
beruhen lassen als ich die gleiche Aussage aus anderem Mund zu hören 
bekam. Seit dem hat mich die Sache nicht ruhig gelassen und deshalb 
wende ich mich an euch, quasi die geballte Kompetenz wenn es um Embedded 
Systems geht. =)

Eventuell verstehe ich hier ja grundsätzlich etwas nicht...

von Axel Jäger (Gast)


Lesenswert?

Ich halte das für überaus schön. Der Bekannte soll sich bitte genauer 
äußern, warum das nicht schön sein soll.

von Lord Z. (lordziu)


Lesenswert?

Mmh, das einzige Argument, dass mir jetzt einfallen würde:

Du verspielst die Möglichkeit, auf dem üblichen Weg (return-Wert) einen 
Fehlercode aus der Funktion zurückgeben zu können. Das ist aber nicht 
nur bei ENUMs als Rückgabewert so, sondern prinzipiell bei allen 
Rückgabewerten (außer signed Typen).

Wenn es andere Gründe gibt interessieren die mich auch sehr.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Lord Ziu schrieb:
> Du verspielst die Möglichkeit, auf dem üblichen Weg (return-Wert) einen
> Fehlercode aus der Funktion zurückgeben zu können.

... sofern die Fehlercodes nicht im enum enthalten sind oder der enum 
sogar nur dafür dient, Fehlercodes zu transportieren.

von Lord Z. (lordziu)


Lesenswert?

Rufus t. Firefly schrieb:
> Lord Ziu schrieb:
>> Du verspielst die Möglichkeit, auf dem üblichen Weg (return-Wert) einen
>> Fehlercode aus der Funktion zurückgeben zu können.
>
> ... sofern die Fehlercodes nicht im enum enthalten sind oder der enum
> sogar nur dafür dient, Fehlercodes zu transportieren.

Das ist natürlich richtig. Aber da man in den meisten Fällen auf mehrere 
mögliche Fehlerfälle reagieren muss (und daher unterschiedliche 
Fehler-Rückgabewerte benötigt) macht es für mich wenig Sinn, die 
Fehlerfälle zusammen mit gültigen Rückgabe-Ergebnissen in einem ENUM zu 
mischen.

Trotzdem fällt mir kein anderer Grund ein, warum man ein ENUM nicht als 
Rückgabewert benutzten sollte.

von Liebling (Gast)


Lesenswert?

Ich mache das auch schon lange so und wüsste keinen Grund, dies nicht so 
zu tun! Mach einfach so weiter.

von Karl H. (kbuchegg)


Lesenswert?

Das einzige was man enums als schlechte Nachrede anhängen könnte ist, 
dass sie in C nur so 'halbe Datentypen' sind. Nicht Fisch, nicht 
Fleisch. Zum Sterben zu viel, zum Leben zu wenig.

enum haben in C dokumentatorischen Wert, werden aber ansonsten wie int 
behandelt. D.h. man kann an eine enum FARBE Variable jederzeit 42 
zuweisen und das ist ja dann nicht Sinn der Sache.

Aber abgesehen von diesem grundsätzlichen Manko, ist mir auch nicht 
klar, warum man keine enum aus einer Funktion returnieren soll. Ich 
wüsste auch keinen irgendwie gerateten Grund oder eine mögliche Falle, 
die man hier umschiffen wollte.

von Detlev T. (detlevt)


Lesenswert?

Also ich halte diese Behauptung auch für Quatsch. Was soll man den sonst 
(formal) zurückgeben? int? AFAIK ist doch ein enum-Wert intern nichts 
anderes als ein int. enum als Rückgabe kann da doch eigentlich nicht 
schaden, zeigt dem Nutzer dafür an, was hier zu erwarten ist.

Und kann ein C-Profi einem C-Amateur wie mir einmal erklären, was ein 
"Pointer auf die Enum" ist, die man da angeblich übergeben (können) 
soll?

von Stefan Hennig (Gast)


Lesenswert?

Also, ein paar Gedanken von mir:

Enum-Typen haben m.E. als einziges Problem, dass es sich um C-Typen 
handelt, du also keinen Einfluss auf die Größe hast. Wenn ich mich recht 
erinnere, ist noch nicht einmal klar, ob es sich um signed oder unsigned 
Werte handelt.
D.h. wenn Du extrem mit dem Speicher geizen musst, kann es ungeschickt 
sein, wenn eine Ampelsteuerung 16 Bit (oder, wie bei Dir 32 Bit) 
verbrät, um ROT, GELB oder GRÜN zu speichern.
Ein echtes NoNo sind Enum-Werte in Strukturen, die im EEPROM abgelegt 
oder serialisiert und verschickt werden sollen. Irgendjemand wird einmal 
die Compilereinstellungen von "Zeit Optimieren" auf "Speicher 
Optimieren" umstellen und plötzlich ist Dein Enum statt 4 Bytes nur noch 
eines groß.

Viele Qualitätsregelwerke verbieten außerdem Casts von Integerwerten auf 
Enums, so dass man sich für die Konvertierung eigene Routinen mit 
switch-Konstrukten schreiben muss, die völlig dämlich aussehen.

Es gibt also schon ein paar Sachen, die man im Hinterkopf behalten 
sollte, aber generell Enums zu verbieten halte ich auch für falsch. 
Schließlich ist es so schon schwer genug, in C gut strukturierten Code 
zu schreiben.

Grüße,
  Stefan

P.S.: Das mit dem Pointer auf Enum adressiert die oben beschriebenen 
Probleme gar nicht, ist also m.E. Mumpitz.

von Karl H. (kbuchegg)


Lesenswert?

Detlev T. schrieb:

> Und kann ein C-Profi einem C-Amateur wie mir einmal erklären, was ein
> "Pointer auf die Enum" ist, die man da angeblich übergeben (können)
> soll?

Gemeint ist höchst wahrscheinlich

anstelle von
1
enum xyz foo()
2
{
3
  ...
4
5
  return value;
6
}

das hier zu verwenden
1
void xyz( enum xyz * retValue )
2
{
3
  ...
4
5
  *retValue = value;
6
}

ok. einen "Vorteil" hat es. Es ist nicht mehr so einfach möglich zu 
ignorieren, dass die Funktion etwas zurückliefert. Zumindest muss der 
Aufrufer eine Variable für den Return-Wert haben, deren Adresse er 
übergeben kann.
Das hat aber mehr mit Programmierdisziplin zu tun, als mit irgendwelchen 
technischen Gründen.

von klaus (Gast)


Lesenswert?

Rainer K. schrieb:
> Er meinte ein guter Programmierer
> übergibt einen Pointer auf die Enum an die Funktion.

Das ist Käse. Wenn dann ein Pointer auf eine Enum Variable (die dann 
global sein müßte). Und wenn immer es geht würde ich globale Variablen 
tunlichst vermeiden.

> ein Kommilitone sagte mir das ich unter keinen Umständen eine
> Enumeration als Rückgabewert verwenden soll und schon garnicht bei Code
> für embedded systeme.

Meiner Erfahrung nach kommen solche "merkwürdigen" Aussagen meist von 
Leuten eigentlich immer nur auf dem gleichen Target und mit dem gleichen 
Compiler gearbeitet haben und nichts anderes kennen (ich habe auch schon 
pauschal gehört, dass Optimierungsstufe 3 fehlerhaften Code erzeugt). In 
der Kombination gibt es dabei vielleicht tatsächlich Probleme mit enums. 
Oder irgendeine Codierrichtlinie verbietet pauschal die Rückgabe von 
enums.

Einen Grund gibt es aber afaik nicht. Ich bevorzuge eher enums da einige 
statische Code-Analysen in der Lage sind wilde Mischungen zwischen 
nativen Typen und Enums (z.B. in Berechnungen) anzumeckern die leicht 
unbeabsichtigt entstehen können. Es ist z.B. schöner an eine Funktion 
ein enum Parameter zu übergeben anstatt int werte...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

klaus schrieb:
> Das ist Käse. Wenn dann ein Pointer auf eine Enum Variable (die dann
> global sein müßte).

Wieso das? Sie muss doch nur im Kontext des Aufrufers gültig sein, kann 
also genausogut eine automatische Variable sein.

> Und wenn immer es geht würde ich globale Variablen tunlichst vermeiden.

Das ist unbestritten oft sinnvoll. Es gibt Ausnahmen, aber die sind 
selten.

von Rainer K. (Gast)


Lesenswert?

Danke für eure vielen Anmerkungen. Beruhigt mich das eigentlich jeder 
meiner Ansicht ist. :-)

von klaus (Gast)


Lesenswert?

Rufus t. Firefly schrieb:
> Wieso das? Sie muss doch nur im Kontext des Aufrufers gültig sein, kann
> also genausogut eine automatische Variable sein.

Müßtest du dann aber als irgendwie Argument der aufzurufenden Funktion 
mitgeben. In diesem Fall sehe ich aber in der Rückgabe dieses Pointers 
(und davon war ja die Rede) keinen Sinn mehr.

von (prx) A. K. (prx)


Lesenswert?

Rufus t. Firefly schrieb:

> Wieso das? Sie muss doch nur im Kontext des Aufrufers gültig sein, kann
> also genausogut eine automatische Variable sein.

Allerdings sind adressierbare lokale Variablen in vielen Fällen 
erheblich teurer als welche, von der keine Adresse benötigt wird.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

A. K. schrieb:
> Allerdings sind adressierbare lokale Variablen in vielen Fällen
> erheblich teurer als welche, von der keine Adresse benötigt wird.

Magst Du das ein bisschen elaborieren?

von Karl H. (kbuchegg)


Lesenswert?

klaus schrieb:
> Rufus t. Firefly schrieb:
>> Wieso das? Sie muss doch nur im Kontext des Aufrufers gültig sein, kann
>> also genausogut eine automatische Variable sein.
>
> Müßtest du dann aber als irgendwie Argument der aufzurufenden Funktion
> mitgeben.

Genau darum geht es doch: Um Komminkation einer Funktion mit ihrer 
Aussenwelt. Wenn du sowieso eine globale Variable hast, brauchst du dir 
über Rückgabe oder Argumenttypen keinen Gedanken mehr machen (Dafür dann 
über 100-tausend andere Dinge, die jetzt aber hier nicht hergehören. 
Globale Variablen sind ein anderes Thema)

> In diesem Fall sehe ich aber in der Rückgabe dieses Pointers
> (und davon war ja die Rede) keinen Sinn mehr.

Ich denke du hast das falsch verstanden.
Es wird kein Pointer zurückgegeben, sondern anstelle eines 
Rückgabewertes soll ein Pointer in die Funktion hineingegeben werden. So 
lautet zumindest der Vorschlag. Warum auch immer.

von Karl H. (kbuchegg)


Lesenswert?

Rufus t. Firefly schrieb:
> A. K. schrieb:
>> Allerdings sind adressierbare lokale Variablen in vielen Fällen
>> erheblich teurer als welche, von der keine Adresse benötigt wird.
>
> Magst Du das ein bisschen elaborieren?

Da sag ich nur:
Premature optimization is the root of all evil.

von Martin (Gast)


Lesenswert?

Premature optimization is the root of all evil.

[Donald Knuth]

von Muetze1 (Gast)


Lesenswert?

Ansonsten als Hinweis: im neuen C++ Standard kann man die 
Zuweisungkompatibilität zu Integer abschalten, wenn man strikte 
Enumerations nutzt. Im obigen Falle dann
1
typedef enum class {
2
        ROT = 1,
3
        BLAU,
4
        GELB,
5
        ORANGE
6
} FARBE;

Alternativ: enum struct. Zuweisungskompatible Typen sind nun nur noch 
die auch oben deklarierten Member der Aufzählung. Dafür ist man aber 
wiederrum gezwungen den Namen der Aufzählung mit anzugeben (es wird wie 
eine Art eigener Namespace umgesetzt), also
1
FARBE lFarbe(FARBE::BLAU);

Aber das nur als Hinweis. Es ist eh C++ und somit hier wohl auch nicht 
von belangen...

von (prx) A. K. (prx)


Lesenswert?

Rufus t. Firefly schrieb:

>> Allerdings sind adressierbare lokale Variablen in vielen Fällen
>> erheblich teurer als welche, von der keine Adresse benötigt wird.
>
> Magst Du das ein bisschen elaborieren?

Variablen mit Adresse landen zwingend im Speicher, wenn lokal dann auf 
dem Stack. Andere Skalare landen oft in Registern. Speicherzugriffe sind 
deutlich teurer. Je nach Architektur kann zudem deutlich Aufwand für den 
Stackframe entstehen.

von (prx) A. K. (prx)


Lesenswert?

Martin schrieb:

> Premature optimization is the root of all evil.
> [Donald Knuth]

Knuth in Ehren, aber den Brunnen erst zudecken wenn das Kind schon 
reingefallen ist, das ist auch kein genialer Ansatz. Ich ziehe es vor, 
erst nachzudenken und dann zu implementieren. Und dazu gehört auch (aber 
nicht nur), die Konsequenzen vorher zu bedenken. Ich bin sicher, dass es 
auch dazu einen Spruch von ihm gibt.

Es gib Leute, die APIs/Libs weitgehend mit teilweise komplexen 
Funktionen und übergebenen Strukturen realisieren. Paradebeispiel dafür 
ist die Firmware-Lib der STM32. Wenn man sowas mal definiert hat, dann 
ist das Kind im Brunnen und nachträgliche Optimierung scheidet der 
Kompatibilität wegen aus. Zudem wird es dadurch auch noch umständlicher 
und zeilenfressender im Umgang.

von Nico22 (Gast)


Lesenswert?

A. K. schrieb:
> Variablen mit Adresse landen zwingend im Speicher

Hups, magst du dafür mal eine Quelle nennen? Es lassen sich doch auch 
Pointer auf den Stack erstellen?

Nico

von (prx) A. K. (prx)


Lesenswert?

Nico22 schrieb:

> Hups, magst du dafür mal eine Quelle nennen? Es lassen sich doch auch
> Pointer auf den Stack erstellen?

Klar doch. Aber ist das etwa kein Speicher?

von Loonix (Gast)


Lesenswert?

Rainer K. schrieb:
> Er meinte ein guter Programmierer
> übergibt einen Pointer auf die Enum an die Funktion.

Sag dem Kerl, ein "guter Programmierer" stellt nicht einfach 
irgendwelche Behauptungen auf ohne diese z.B.durch Angabe eines 
Beispiels o.ä. verifizierbar zu machen. Wenn man schon einen Satz so 
beginnen hört, möchte man den Rest doch gar nicht mehr hören - diesen 
Umstand nutzen die Möchtegerns aus um sich wichtig zu machen.

Just my 2 Cents

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.