Forum: Compiler & IDEs Anfängerfrage zu Unions


von Janvi (Gast)


Lesenswert?

habe mich im aktuellen Projekt mal an Unions herangetraut und es hat 
sogar wie erwartet auf Anhieb funktioniert. Mit nachstehender 
Konstruktion kann ich auf einen Sendepuffer wahlweise als Array (z. Bsp. 
zum Berechnen einer Prüfsumme) oder auf einzelne Messwerte mit deren 
Namen zugreifen.
1
union  TxArrayWortConverter            // Union Name
2
    {int16_t WortArray[TelLen];          // Arrayzugriff ueber Index mit DatTxBuf.WortArray[Index]
3
    struct SondenTel  {  uint16_t  lfdNr;  // 0 Einzelwert Zugriff ueber DatTxBuf.EinzelWert.lfdNr
4
                uint16_t  Flow;  // 1
5
                uint16_t  Temp;  // 2
6
                uint16_t  Volt;  // 3
7
                uint16_t  iTmp;  // 4
8
                uint16_t  Leak;  // 5
9
                uint16_t  nc6;   // 6
10
                uint16_t  ChkSum;  // 7
11
              }  EinzelWert;
12
    }DatTxBuf;                      // Reservierung von 32 Bytes unter Variablennamen DatTxBuf

Nun hat die Sache aber mehrere Nachteile bzw ist aus mehreren Gründen 
etwas unübersichtlich.

1) Der struct SondenTel wird innerhalb der Union definiert. Das ist 
schlecht, wenn weitere Variablen mit dem gleichen Telegramminhalt an 
andere Stelle reserviert werden sollen.

2) Die Reservierung des Puffers "DatTxBuf" erfolgt zusammen mit der 
Definition des Unions. Das ist schlecht, wenn weitere Puffer z. Bsp. für 
andere Uarts reserviert werden sollen.

Jetzt habe ich angenommen, daß der Union Name "TxArrayWortconverter" ein 
neuer Typname ist, unter welchem Variablen reserviert werden könnnen. 
D.h. in der Union Definition lasse ich am Ende "DatTxBuf" weg - der 
Strichpunkt bleibt.  Mit "volatile TxAarrayConverter DatTxBuf2" kommt 
eine für mich unverständliche Meldung: error: expected '=', ',', ';', 
'asm' or '__attribute__' before 'DatTxBuf2'

Brauche ich da noch typedef - falls ja wo ?

von Karl H. (kbuchegg)


Lesenswert?

Janvi schrieb:

> 1) Der struct SondenTel wird innerhalb der Union definiert. Das ist
> schlecht, wenn weitere Variablen mit dem gleichen Telegramminhalt an
> andere Stelle reserviert werden sollen.

Kein Mensch zwingt dich dazu, dass du die struct innerhalb der Union 
definieren musst.

> 2) Die Reservierung des Puffers "DatTxBuf" erfolgt zusammen mit der
> Definition des Unions. Das ist schlecht, wenn weitere Puffer z. Bsp. für
> andere Uarts reserviert werden sollen.

Kein Mensch zwingt dich dazu, dass so zu machen. Deklariere die union 
efinach mal und dann legst du beliebig viele Variablen davon an.


> Jetzt habe ich angenommen, daß der Union Name "TxArrayWortconverter" ein
> neuer Typname ist, unter welchem Variablen reserviert werden könnnen.
> D.h. in der Union Definition lasse ich am Ende "DatTxBuf" weg - der
> Strichpunkt bleibt.  Mit "volatile TxAarrayConverter DatTxBuf2" kommt
> eine für mich unverständliche Meldung: error: expected '=', ',', ';',
> 'asm' or '__attribute__' before 'DatTxBuf2'

In C sind weder bei Strukturen noch bei Unions die Namen der struct bzw 
der union automatisch neue Typnamen. Du musst immer struct bzw union 
dazuschreiben
1
struct Date
2
{
3
   int  day;
4
   int  month;
5
   int  year;
6
};
7
8
9
struct Date  birthDay;
10
struct Date  weddingDay;
11
12
uint8_t dayOfWeek( struct Date* date )
13
{
14
   ...
15
}
>
> Brauche ich da noch typedef - falls ja wo ?

ein typedef vereinfacht das ganze ein wenig, weil du damit aus einem 
"struct Date" einen neuen Datentyp machen kannst
1
struct Date
2
{
3
   int  day;
4
   int  month;
5
   int  year;
6
};
7
8
typedef struct Date Date_;
9
10
Date_  birthDay;
11
Date_  weddingDay;
12
13
uint8_t dayOfWeek( Date_* date )
14
{
15
   ...
16
}

Du solltest ins Auge fassem, dir ein C-Buch zuzulegen. Deine Frage zeigt 
ganz deutlich
* dass du kein Buch hast
* dass dir wesentliche Konzepte wie C funktioniert verständnismässig
  noch nicht klar sind. Und zwar solche Konzepte, die dir jedes Buch
  in 10 Minuten lesen näher bringt.
* Du verwendest die dicht gepackten Kurzformen, die dir C erlaubt,
  ohne dir darüber klar zu sein, dass es sich um Kurzformen handelt
  und wie die zugehörigen Langformen aussehen. Der normale Lernweg
  ist aber genau anders herum. Erst lernt man die Langform und dann
  lernt man, in welchen Fällen diese durch die kürzeren Formen
  ersetzt werden können.
1
struct SondenTel
2
{
3
  uint16_t  lfdNr;  // 0 Einzelwert Zugriff ueber DatTxBuf.EinzelWert.lfdNr
4
  uint16_t  Flow;  // 1
5
  uint16_t  Temp;  // 2
6
  uint16_t  Volt;  // 3
7
  uint16_t  iTmp;  // 4
8
  uint16_t  Leak;  // 5
9
  uint16_t  nc6;   // 6
10
  uint16_t  ChkSum;  // 7
11
};
12
13
union  TxArrayWortConverter            // Union Name
14
{
15
  int16_t          WortArray[sizeof(struct SondenTel)];          // Arrayzugriff ueber Index mit DatTxBuf.WortArray[Index]
16
  struct SondenTel EinzelWert;
17
};
18
19
20
struct SondenTel Telegram1;
21
struct SondenTel Telegram2;
22
union TxArrayWortConverter DatTxBuf;
23
union TxArrayWortConverter DatRxBuf;
24
25
26
// oder mit typedef zur Verkürzung und Vereinfachung der Schreibweise
27
typedef struct SondenTel SondenTel_;
28
typedef union TxArrayWortConverter ArrayConverter_;
29
30
SondenTel_ Telegram1;
31
SondenTel_ Telegram2;
32
ArrayConverter_ DatTxBuf;
33
ArrayConverter_ DatRxBuf;

wie immer gilt:
Der Compiler liest den Text von oben nach unten. Und zwar nur ein 
einziges mal. Bevor man etwas verwenden kann, muss es erst dem Compiler 
bekannt gemacht werden. D.h. Ab dem Zeitpunkt an dem du den typedef 
machst, kannst du dann im Text darunter überall anstelle von "struct 
SondenTel" den durch den typedef dafür vereinbarten kürzeren Typnamen 
"SondenTel_" benutzen.
Und das ist mit allen Dingen so: Erst dem Compiler bekannt machen, dann 
benutzen. Und zwar von oben nach unten im Text.
Wenn du daher den typedef vorziehst, direkt hinter den Punkt an dem die 
struct SondenTel bekannt ist, dann kannst du in weiterer Folge überall 
den neuen Typnamen benutzen.
1
struct SondenTel
2
{
3
  uint16_t  lfdNr;  // 0 Einzelwert Zugriff ueber DatTxBuf.EinzelWert.lfdNr
4
  uint16_t  Flow;  // 1
5
  uint16_t  Temp;  // 2
6
  uint16_t  Volt;  // 3
7
  uint16_t  iTmp;  // 4
8
  uint16_t  Leak;  // 5
9
  uint16_t  nc6;   // 6
10
  uint16_t  ChkSum;  // 7
11
};
12
13
typedef struct SondenTel SondenTel_;
14
15
union  TxArrayWortConverter            // Union Name
16
{
17
  int16_t    WortArray[sizeof(struct SondenTel)];          // Arrayzugriff ueber Index mit DatTxBuf.WortArray[Index]
18
  SondenTel_ EinzelWert;
19
};

und weil das häufig so ist, gibt es dann die Möglichkeit, die 
Strukturdefinition und den typedef zusammenzuziehen:
1
typedef struct SondenTel
2
{
3
  uint16_t  lfdNr;  // 0 Einzelwert Zugriff ueber DatTxBuf.EinzelWert.lfdNr
4
  uint16_t  Flow;  // 1
5
  uint16_t  Temp;  // 2
6
  uint16_t  Volt;  // 3
7
  uint16_t  iTmp;  // 4
8
  uint16_t  Leak;  // 5
9
  uint16_t  nc6;   // 6
10
  uint16_t  ChkSum;  // 7
11
} SondenTel_;
12
13
union  TxArrayWortConverter            // Union Name
14
{
15
  int16_t    WortArray[sizeof(struct SondenTel)];          // Arrayzugriff ueber Index mit DatTxBuf.WortArray[Index]
16
  SondenTel_ EinzelWert;
17
};

und weil da jetzt der eigentliche Name der Struktur keine Rolle mehr 
spielt, gibt es in C die Möglichkeit einer anonymen struct, d.h. einer 
struct die keinen eigenen Namen mehr hat sondern nur noch über den 
mittels typedef eingeführten Datentypnamen angesprochen werden kann:
1
typedef struct
2
{
3
  uint16_t  lfdNr;  // 0 Einzelwert Zugriff ueber DatTxBuf.EinzelWert.lfdNr
4
  uint16_t  Flow;  // 1
5
  uint16_t  Temp;  // 2
6
  uint16_t  Volt;  // 3
7
  uint16_t  iTmp;  // 4
8
  uint16_t  Leak;  // 5
9
  uint16_t  nc6;   // 6
10
  uint16_t  ChkSum;  // 7
11
} SondenTel_;
12
13
union  TxArrayWortConverter            // Union Name
14
{
15
  int16_t    WortArray[sizeof(struct SondenTel)];          // Arrayzugriff ueber Index mit DatTxBuf.WortArray[Index]
16
  SondenTel_ EinzelWert;
17
};

genauso wie es häufig vorkommt, dass man bei der Definition einer struct 
auch gleich Variablen mit erzeugen möchte
1
struct SondenTel
2
{
3
  uint16_t  lfdNr;  // 0 Einzelwert Zugriff ueber DatTxBuf.EinzelWert.lfdNr
4
  uint16_t  Flow;  // 1
5
  uint16_t  Temp;  // 2
6
  uint16_t  Volt;  // 3
7
  uint16_t  iTmp;  // 4
8
  uint16_t  Leak;  // 5
9
  uint16_t  nc6;   // 6
10
  uint16_t  ChkSum;  // 7
11
};
12
13
struct SondenTel Telegram1;
14
struct SondenTel Telegram2;

C kommt dir da insofern entgegen, als man die Varieblendefinitonen 
gleich an die Strukturdefinition anhängen kann und so zumindest an 
dieser Stelle nicht alles 2 mal tippen muss:
1
struct SondenTel
2
{
3
  uint16_t  lfdNr;  // 0 Einzelwert Zugriff ueber DatTxBuf.EinzelWert.lfdNr
4
  uint16_t  Flow;  // 1
5
  uint16_t  Temp;  // 2
6
  uint16_t  Volt;  // 3
7
  uint16_t  iTmp;  // 4
8
  uint16_t  Leak;  // 5
9
  uint16_t  nc6;   // 6
10
  uint16_t  ChkSum;  // 7
11
} Telegram1, Telegram2;


Alles über struct gesagte gilt sinngemäss genauso für union. Die beiden 
unterscheiden sich in dieser Hinsicht syntaktisch nicht. Aus C Sicht 
sind sowohl struct als auch union einfach nur Zusammenfassungen von 
Daten, wobei eine struct die Dinge hintereinander und eine union die 
übereinander anordnet. Aber wie diese Anordnung im Speicher ganz konkret 
aussieht, darüber gibt dir der C-Standard keine Garantie. Der Compiler 
hat da so einige Freiheiten.

von Janvi (Gast)


Lesenswert?

> dass du kein Buch hast

habe das C Referenz Handbuch von Olaf Hartwig. Leider sind die Beispiele 
dadrin manchmal besch.. und einige sogar falsch. Deshalb guck ich immer 
im Netz wo es mehr als ein Dutzend C Kurse mit kinderlieben Beispielen 
gibt.

> dass dir wesentliche Konzepte verständnismässig noch nicht klar sind.

deshalb auch "Anfängerfrage".

Mit deinen Ausfühungen ist mir die Verwendung von struct bzw. union 
Namen bzw. deren Überführung in Typen klarer geworden. Im Buch hätte ich 
da wahrscheinlich noch 5 mal ohne Erfolg gelesen. C ist wie beim 
Jonglieren oder Klavierspielen, Skifahren usw: Nur lesen nützt nix! Man 
muss eben auch noch genug abgucken, nachmachen und regelmässig üben um 
das Projekt wirklich zum Fliegen zu kriegen. Manchmal hilft es da schon 
zum Verständnis, einen Code mit eigenen Namen anstelle einem abstrakten 
Beispiel (Hartwigs Lieblingsvariablen sind x,y,z) anzuschauen.

Jedenfall hat es nach dem Lesen deiner Ausführungen sofort prima 
geklappt. Vorerst mal ohne typdef um noch etwas Übung zu kriegen. Wenn 
ich dann mal  structs und unions im Schlaf verschachteln kann wird 
typedef meine nächste Lieblingsanweisung werden

von Karl H. (kbuchegg)


Lesenswert?

Janvi schrieb:
>> dass du kein Buch hast
>
> habe das C Referenz Handbuch von Olaf Hartwig. Leider sind die Beispiele
> dadrin manchmal besch.. und einige sogar falsch. Deshalb guck ich immer
> im Netz wo es mehr als ein Dutzend C Kurse mit kinderlieben Beispielen
> gibt.

Kernighan & Ritchie
Programmieren in C



> Namen bzw. deren Überführung in Typen klarer geworden. Im Buch hätte ich
> da wahrscheinlich noch 5 mal ohne Erfolg gelesen.

Spricht nicht gerade für das Buch. Das ist noch eine der leichteren 
Übungen. Ich kenn das Buch nicht und ich finde online keine 
Inhaltsangabe. Was micht stutzig macht: Das Machwerk ist von 1992. Das 
muss so ja noch nicht allzuviel heißen. Aber das es in der langen Zeit 
keine Rezension dafür gibt und das Teil mitlerweile antiquarisch 
gehandelt wird, spricht meiner Meinung nach für sich.
Auch macht mich der Titel stutzig. "Referenzhandbuch" klingt für mich 
eher nach einem Nachschlagewerk ala Brockhaus. D.h. zum Nachschlagen von 
Details brauchbar, aber zum Erstlernen nicht wirklichg geeignet weil es 
keinen didaktisch aufbereitetenten roten Faden durch die einzelnen 
Themenkreise gibt. Aber wie gesagt: Ich kenne das Buch nicht, daher kann 
ich mir hier auch nicht wirklich ein Urteil erlauben.

von open book (Gast)


Lesenswert?

eine weitere Variante:
http://openbook.galileocomputing.de/c_von_a_bis_z/015_c_strukturen_009.htm#mj9e030e6d45f3619a80b6814a2ba14bea
passt nur ins Regal, wenn der Laptop zu ist ;-)

von Michael B. (mb_)


Lesenswert?

Karl Heinz Buchegger schrieb:
>
1
> union  TxArrayWortConverter            // Union Name
2
> {
3
>   int16_t          WortArray[sizeof(struct SondenTel)];          //
4
> Arrayzugriff ueber Index mit DatTxBuf.WortArray[Index]
5
>   struct SondenTel EinzelWert;
6
> };
7
>

Damit reservierst du doppelt soviel Speicher wie nötig.
Besser:

int16_t          WortArray[sizeof(struct SondenTel) / sizeof(int16_t)]

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Michael Buesch schrieb:
> Damit reservierst du doppelt soviel Speicher wie nötig.
> Besser:
>
> int16_t          WortArray[sizeof(struct SondenTel) / sizeof(int16_t)]

Und was geschieht, wenn sizeof (struct SondenTel) ungerade ist?
Besser wäre es, das Array dann noch ein Element größer anzulegen ...

von Klaus W. (mfgkw)


Lesenswert?

Solange da nur uint16_t drinstehen, ist das nicht besonders 
wahrscheinlich.
Deshalb will er ja parallel ein Feld mit den einzelnen uint16_t haben 
:-)

Zumal die union davon auch nicht kleiner wird, weil die Größe der struct 
ja dann die Gesamtgröße dominiert.
Genau genommen würde auch ein Feldelement reichen, der Rest geht als 
Feldgrenzenüberschreitung sogar gut, solange er nicht über die struct 
hinausgeht.

von Karl H. (kbuchegg)


Lesenswert?

Michael Buesch schrieb:
> Karl Heinz Buchegger schrieb:
>>
1
>> union  TxArrayWortConverter            // Union Name
2
>> {
3
>>   int16_t          WortArray[sizeof(struct SondenTel)];          //
4
>> Arrayzugriff ueber Index mit DatTxBuf.WortArray[Index]
5
>>   struct SondenTel EinzelWert;
6
>> };
7
>>
>
> Damit reservierst du doppelt soviel Speicher wie nötig.

Hast recht.
Da sollte besser der Datentyp uint8_t benutzt werden. Hab ich übersehen.

von Michael B. (mb_)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Michael Buesch schrieb:
>> Damit reservierst du doppelt soviel Speicher wie nötig.
>> Besser:
>>
>> int16_t          WortArray[sizeof(struct SondenTel) / sizeof(int16_t)]
>
> Und was geschieht, wenn sizeof (struct SondenTel) ungerade ist?

Diese Frage ergibt überhaupt keinen Sinn, für diesen Anwendungsfall mit 
einem union aus struct und array.

von Martin (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> und weil da jetzt der eigentliche Name der Struktur keine Rolle mehr
> spielt, gibt es in C die Möglichkeit einer anonymen struct,

Aber Vorsicht: Damit sind dann keine forward declarations mehr möglich. 
Ich bestehe daher üblicherweise auf eigenen Namen für die structs. :-)

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.