Forum: Compiler & IDEs Simple C Frage zum Typ enum


von David H. (davidm)


Lesenswert?

Ich dachte bisher das enum eine Art Aufzählung sei

z.B. rot, grün, blau.
1
typedef enum
2
{
3
    ETH_LINK_ST_DOWN        = 0x0
4
    ETH_LINK_ST_UP = 0x1
5
    ETH_LINK_ST_LP_NEG_UNABLE  = 0x2
6
    ETH_LINK_ST_REMOTE_FAULT = 0x4
7
    ETH_LINK_ST_LP_NEG_UNABLE .... 
8
    ETH_LINK_ST_PDF ....
9
    ETH_LINK_ST_LP_PAUSE ....
10
    ETH_LINK_ST_LP_ASM_DIR
11
    ETH_LINK_ST_NEG_TMO
12
    ETH_LINK_ST_NEG_FATAL_ERR
13
} eEthLinkStat;  (linkStat)

Ich dachte bisher auch das z.B. linkStat nur einen wert haben kann.


z.B. linkStat =  ETH_LINK_ST_UP

oder

linkStat = ETH_LINK_ST_DOWN



Nur scheinbar greifen die jungs da auf einzelne Bits zu.

Wird hier linkStat jedesmal überschrieben? nur da wäre die oder 
verknüpfung mit |= so ziemlich fuern arsch.
Oder werden hier tatsächlich nur einzelne Bits gesetzt?

Scheinbar haben die das bei der deklaration von linkStat genau so 
gewollt da die jedesmal genau ein Bit festgelegt haben

1
      lpAD.w=_PhyReadReg(PHY_REG_ANLPAD, _PhyAdd);
2
      if(lpAD.REM_FAULT)
3
      {
4
        linkStat|=ETH_LINK_ST_REMOTE_FAULT;
5
      }
6
      
7
      if(lpAD.PAUSE)
8
      {
9
        linkStat|=ETH_LINK_ST_LP_PAUSE;
10
        lp_Pause=1;
11
      }
12
      if(lpAD.ASM_DIR)
13
      {
14
        linkStat|=ETH_LINK_ST_LP_ASM_DIR;
15
        lp_AsmDir=1;
16
      }

von Walter T. (nicolas)


Lesenswert?

Ah, man sieht das Verständnis wächst gerade beim Formulieren.

LinkStat hat immer nur einen Wert, in diesem Fall eben ein zugewiesenes 
Bitmuster.

von Christian (Gast)


Lesenswert?

ETH_LINK_ST_UP hat den Wert 0x1 und nicht 0x2.
Per Definition hat der erste Eintrag den Wert 0 im Enum. Hex, dez oder 
bin ist ja zuerstmal egal. Eintrag zwei hat dann den Wert 1, Eintrag 
drei den Wert 2 usw.....
Du hast recht in linkStat ist nur ein Wert gespeichert, da es ja auch 
nur EINE Variable ist und kein Array. Und den Rest kannst du dir doch 
ausrechnen. 0x2 verodert mit 0x4 schreibst du einfach in binär um und 
tust die ganzen 1er und 0er verodern. Das Ergebnis rechnest du in Hex 
zurück und schon siehst du welchen Wert aus dem Enum in linkStat 
enthalten ist

von NurEinGast (Gast)


Lesenswert?

Nun ...

Wenn Du den Enum "in Ruhe lässt", dann zählt er in der Tat einfach hoch
1
typedef enum
2
{
3
    ETH_LINK_ST_DOWN, 
4
    ETH_LINK_ST_UP,
5
    ETH_LINK_ST_LP_NEG_UNABLE,  
6
    ETH_LINK_ST_REMOTE_FAULT 
7
....
dann wäre
    ETH_LINK_ST_DOWN  --> 0
    ETH_LINK_ST_UP    --> 1
    ETH_LINK_ST_LP_NEG_UNABLE   --> 2
    ETH_LINK_ST_REMOTE_FAULT    --> 3
    .......

Nun haben aber die Jungs immer genau so einen Wert zugewiesen,
so dass ein Bit gesetzt wird.
1
    ETH_LINK_ST_DOWN        = 0x0,    -> Nichts gesetzt 
2
    ETH_LINK_ST_UP = 0x1,             -> Bit 0 gesetzt
3
    ETH_LINK_ST_LP_NEG_UNABLE  = 0x2, -> Bit 1 gesetzt
4
    ETH_LINK_ST_REMOTE_FAULT = 0x4,   -> Bit 2 gesetzt

von David H. (davidm)


Lesenswert?

ok, nun gut, mal zum verständnis, wie unterscheidet sich eine 
union/structure von enum?

1
typedef union {
2
  struct {
3
    unsigned PROT_SEL:5;    
4
    unsigned BASE10T:1;
5
    unsigned BASE10T_FDX:1;
6
    unsigned BASE100TX:1;
7
    unsigned BASE100TX_FDX:1;
8
    unsigned BASE100T4:1;
9
    unsigned PAUSE:1;      // NOTE: the PAUSE fields coding for SMSC is reversed!
10
    unsigned ASM_DIR:1;      // typo in the data sheet?  
11
    unsigned :1;
12
    unsigned REM_FAULT:1;
13
    unsigned :1;
14
    unsigned NP_ABLE:1;
15
  };
16
  struct {
17
    unsigned short w:16;
18
  };
19
}

ok mal davon abgesehen das union genommen wird um den Speicherbereich zu 
Teilen geht es mir nur um die struct selbst. hier wird wieder auf 
einzelne Bits zugegriffen. Warum wählte der Programmierer aber in diesem 
Fall eine structure und nicht eine enumeration?

von Karl H. (kbuchegg)


Lesenswert?

David Mueller schrieb:
> Ich dachte bisher das enum eine Art Aufzählung sei
>
> z.B. rot, grün, blau.

Jain.
Tatsächlich ist eine enum nur eine glorifizierte Variante, wie man einen 
Integer schreiben kann.

Nichts und niemand hindert dich daran

  linkStat = 5;

zu schreiben.


> Wird hier linkStat jedesmal überschrieben? nur da wäre die oder
> verknüpfung mit |= so ziemlich fuern arsch.

Achtung!
Das Geheimnis liegt darin, dass du den einzelnen Aufzählngswerten in 
einem enum selbst tatsächliche Integer Werte verpassen kannst

In
enum farbe
{
  rot,
  gruen,
  blau
};

sind die Integer Werte für rot, grün und blau tatsächlich 0, 1, 2

Was allerdings niemanden hindert
1
  enum farbe color = blau | gruen;
zu schreiben. Es werden dann eben die Bitmuster miteinander verodert, 
genauso wie es auch bei einem unsigned int der Fall wäre. 0x01 | 0x02 
ergibt 0x03. Es gibt keinen Aufzählungswert im enum, der 0x03 wäre. Aber 
das hindert niemanden daran, diese 0x03 an color zuzuweisen. Das Problem 
ist, dass du im Nachhinein nicht mehr identifizieren kannst, ob die 0x03 
sich jetzt aus einer Kombination aus rot+grün+blau zusammen gesetzt hat, 
oder ob es nur grün und blau war. Und bei mehr Werten und mehr 
Kombinationen wirdsd noch uneindeutiger.

Aber!
Du kannst auch dafür sorgen, dass du rot, grün und blau andere Integer 
Werte gibst.
1
enum farbe
2
{
3
  rot    = 0x01,
4
  gruen  = 0x02,
5
  blau   = 0x04
6
};


und jetzt kannst du die, genauso wie bei Integern einfach zusammenodern.

enum werden in C generell überschätzt. Das ganze Konzept ist (*) nur 
halbherzig durchgezogen. Im Grunde ist ein enum nichts anderes als ein 
nicht vom Präprozessor abhängiges
1
#define ROT   0x01
2
#define GRUEN 0x02
3
#define BLAU  0x04

Mehr steckt da nicht wirklich dahinter. Enums haben ihren Zweck, weil 
man in einer Argumentliste
1
void Set( enum farbe col )
dann dokumentiert hat, welche Werte hier erwartet werden, nämlich die 
die im enum enthalten sind. Aber mehr ist das nicht. Niemand hindert 
dich daran, diese Funktion mit einem Wert von 78 aufzurufen.


(*) Im Vergleich zu anderen Sprachen wie zb Pascal, die ja auch über 
Mengen-Datentypen verfügen.

von Karl H. (kbuchegg)


Lesenswert?

David Mueller schrieb:
> ok, nun gut, mal zum verständnis, wie unterscheidet sich eine
> union/structure von enum?

komplett.
völlig andere Baustelle

mit einem enum beschreibst du eine Menge von 'möglichen' Werten. Zum 
Beispiel die Kartenfarben oder möglichen Kartenwerte von Spielkarten.
Das hat noch nichts mit Speicher oder Speicherform oder 
Speicheranforderung zu tun.

In

   variable = wert

Ist die 'struct' ein Mittel um die Eigenschaften von 'variable' zu 
beschreiben. Ein enum hingegen beschreibt die Eigenschaften von 'wert'.

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


Lesenswert?

Aber wirklich Sinn hat so eine Aktion nicht. Der beste Effekt, den
enums sonst noch haben ist, dass Debugger die Werte dann symbolisch
darstellen können. Wenn man nun aber mehrere Werte verODERt, dann
entstehen daraus Werte, die keinen Eintrag im enum-Typ haben, dann
kann man auch gleich "int" schreiben.

Wenn man das Zeug verodern will, sollte man Bitfelder nehmen.

von Christian (Gast)


Lesenswert?

Ich setze Enums gerne ein um States zu definieren
1
typedef enum
2
   {
3
   PID_NIL = 0;
4
   PID_NotInUse,
5
   PID_Active,
6
   PID_Error
7
   } PID_STATUS_E;

Eine Struktur setzt man dann für verschiedene Variablen ein, die aber 
über eine Variable zugreifbar sind
1
typedef struct
2
   {
3
   byte Kp;
4
   byte Ki;
5
   byte Kd;
6
   byte Error;
7
   byte Command;
8
   PID_STATUS_E eStatus;
9
   }PID_CONTROLLER;


Nun noch in der main/Funktion die Variablen benutzen
1
PID_CONTROLLER tController;
2
tController.Kp = 2;
3
tController.Ki = 3;
4
tController.Kd = 4;
5
tController.eStatus = PID_NotInUse;

Somit hast du alle nötigen Daten für einen Regler in einer Variable. Das 
mit den States nutz man vor allem beim Debugging

von David H. (davidm)


Lesenswert?

Danke für die ganzen Antworten!


Jörg Wunsch schrieb:
> Wenn man nun aber mehrere Werte verODERt, dann
> entstehen daraus Werte, die keinen Eintrag im enum-Typ haben, dann
> kann man auch gleich "int" schreiben.

das war mein Problem warum ich es nicht verstand.
MPLABX schreibt dann in der variablen liste beim debuggen nur noch 
"enumeration?" aus weil er es ebend nicht zuordnen kann.

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


Lesenswert?

Christian schrieb:
> Ich setze Enums gerne ein um States zu definieren

Dafür ist es ja auch sinnvoll.

Sowie da aber irgendwie ein "|"-Operator ins Spiel kommt, hat die
Sache mit einem enum keinen Sinn mehr.  Dann kann man die
Komponenten-Konstanten auch gleich mit #define vereinbaren. Klar,
vielleicht gibt es irgendwo Codierungs-Regeln, die besagen: „#define
ist übel, benutze enum stattdessen“, aber das sture Befolgen dieser
Regel bringt an dieser Stelle keinen Gewinn irgendeiner Art. Ein
Bitfield wäre dann u. U. die bessere Lösung.

von Andreas B. (andreas_b77)


Lesenswert?

Jörg Wunsch schrieb:
> Dann kann man die
> Komponenten-Konstanten auch gleich mit #define vereinbaren. Klar,
> vielleicht gibt es irgendwo Codierungs-Regeln, die besagen: „#define
> ist übel, benutze enum stattdessen“,

enums sind Teil dessen, was der Compiler sieht, #defines nicht. Das 
heißt auch, dass ein Debugger über enums und dessen Symbole Bescheid 
weiß, während #defines einfach nur Literale einsetzen und die 
Information über die Namen verloren geht.


Kann es sein, dass aktuelle Versionen von gcc/gdb mittlerweile auch 
Präprozessor-Symbole irgendwie in den Debugging-Informationen eintragen? 
Habe ich gerade nicht ausprobiert.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas B. schrieb:

> enums sind Teil dessen, was der Compiler sieht, #defines nicht. Das
> heißt auch, dass ein Debugger über enums und dessen Symbole Bescheid
> weiß, während #defines einfach nur Literale einsetzen und die
> Information über die Namen verloren geht.
>
> Kann es sein, dass aktuelle Versionen von gcc/gdb mittlerweile auch
> Präprozessor-Symbole irgendwie in den Debugging-Informationen eintragen?

Ja, spätestens seit DWARF-3.

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


Lesenswert?

Andreas B. schrieb:
> Das heißt auch, dass ein Debugger über enums und dessen Symbole Bescheid
> weiß

Das hilft aber nicht, wenn du mit diesen enum-Werten etwas anderes
als eine reine Zuweisung / einen Test machst.  Insbesondere also,
wenn du mit den Dingern rechnest (Bitoperationen hier im Beispiel),
dann kommen da eben u. U. Werte raus, die gar kein enum-Äquivalent
besitzen.

Die einzige Rechnung, die Sinn hat, wäre ein Inkrement oder ein
Dekrement bis zum Ende bzw. Anfang, vorausgesetzt, dass die einzelnen
enum-Werte lückenlos sind (wie es bspw. bei einer Aufzählung ohne
explizite Wertzuweisung passiert).

Johann L. schrieb:
> Ja, spätestens seit DWARF-3.

Schon bei stabs so gewesen.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Für mich ein großer Vorteil von enum's ist die behandlung in case/switch 
statements: Hier warnt der Compiler, wenn du gewisse Werte nicht 
abfängst.

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Jörg Wunsch schrieb:
> Johann L. schrieb:
>> Ja, spätestens seit DWARF-3.
>
> Schon bei stabs so gewesen.

Echt? Ich hab mal Probeweise die gleiche C-Quelle einmal mit stabs und 
einmal mit dwarf-3 compiliert. In der dwarf-3 sind deutlich mehr Infos 
und die Werte aller Makros; auch der built-in Makros des Compilers. Bei 
stans fehlen diese, und auch das

#define ABCD 0

findet sich nicht, welches im dwarf-3 erscheint als

.LASF1638:
  .string  "ABCD 0"

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


Lesenswert?

Michael Reinelt schrieb:
> Für mich ein großer Vorteil von enum

Wir brauchen jetzt hier nicht allgemein über Vorteile von enums zu
philosophieren.  Davon abgesehen, dass das in C (leider) trotzdem
nur ein Alias für “int” ist, sind diese klar. Hier ging es ganz
konkret um einen Missbrauch von enums für Dinge, für die er weder
konzipiert worden ist noch in irgendeiner Weise Sinn hat.

Wenn du jetzt nicht weißt, was ich damit meine, dann lies dir bitte
den Rest des Threads erstmal durch.

Johann L. schrieb:
>>> Ja, spätestens seit DWARF-3.
>> Schon bei stabs so gewesen.
>
> Echt? Ich hab mal Probeweise die gleiche C-Quelle einmal mit stabs und
> einmal mit dwarf-3 compiliert. In der dwarf-3 sind deutlich mehr Infos
> und die Werte aller Makros

Makros sind ja auch eine andere Baustelle.  Aber enum-Elemente gibt's
wirklich schon lange, auch in den Debuginfos.  Mir ist sogar so, als
ob diese in den native COFF debug infos drin gewesen wären.  Die
haben eigentlich schon seit 25 Jahren keine Relevanz mehr (weil sie
bereits C89s Erweiterungen wie Prototypen nicht abbilden können),
die sind aber beim AVR durch AVR Studio 4 seinerzeit nochmal
aufgewärmt worden, bevor das Studio endlich ELF und DWARF verstanden
hat. (Die Unixe haben zwar noch ein Weilchen COFF benutzt damals,
aber als Debuginfo dann bereits stabs da reingepackt, denn das ging
auch mit COFF schon.)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Johann L. schrieb:
>>>> Ja, spätestens seit DWARF-3.
>>> Schon bei stabs so gewesen.
>>
>> Echt? Ich hab mal Probeweise die gleiche C-Quelle einmal mit stabs und
>> einmal mit dwarf-3 compiliert. In der dwarf-3 sind deutlich mehr Infos
>> und die Werte aller Makros
>
> Makros sind ja auch eine andere Baustelle.  Aber enum-Elemente gibt's
> wirklich schon lange, auch in den Debuginfos.

Ok, meine Aussage bezug sich auf "Präprozessor-Symbole" wo ich davon 
ausging, daß Makros damit gemeint sind:

Andreas B. schrieb:
> Kann es sein, dass aktuelle Versionen von gcc/gdb mittlerweile auch
> Präprozessor-Symbole irgendwie in den Debugging-Informationen eintragen?
> Habe ich gerade nicht ausprobiert.

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


Lesenswert?

Johann L. schrieb:
> Ok, meine Aussage bezug sich auf "Präprozessor-Symbole"

Mea culpa, diesmal hatte ich nicht richtig gelesen. Ja, die gibt's
natürlich erst in aktuellem DWARF. Wobei gegenüber dem enum
natürlich für den Debugger trotzdem nicht die Möglichkeit besteht,
ein Objekt vom Typ "int" dann automatisch mit seinem Makrowert
darzustellen, denn er dürfte keine Ahnung haben, ob das Objekt nun
ausschließlich durch die Verwendung mit Makros bearbeitet worden ist
oder nicht. Selbst wenn, wird es oft genug mehrere Makros für den
gleichen Wert geben (NULL oder NO_VALUE zum Beispiel), da könnte
der Debugger auch nicht entscheiden, welchen Makro er benutzen soll.

Der einzige Wert, den ich da sehe wäre, dass man im Debugger dann
schreiben kann:
1
(gdb) set myvar = MYVALUE1

weil er jetzt eine Kennung über den Wert von Makro MYVALUE1 hat (aber
das allein ist natürlich schon mal nett gegenüber früher).

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.