Forum: Compiler & IDEs Union Initialsierung


von Sebastian B. (wastel)


Lesenswert?

Hallo,
ich habe folgendes Problem. Ich erzeuge ein Union und möchte dieses
gleiche initialisieren, dies schlägt aber aus mir nicht erklärlichen
Gründen fehl. Ich hoffe der ein oder andere kann mir behilflich sein.

Listing:

typedef union
{
  unsigned char all[2];

  struct
    {
      unsigned char bit23:1;
      unsigned char bit22:1;
      unsigned char bit21:1;
      unsigned char bit20:1;
      unsigned char bit19:1;
      unsigned char bit18:1;
      unsigned char bit17:1;
      unsigned char bit16:1;
      unsigned char bit15:1;
      unsigned char bit14:1;
      unsigned char bit13:1;
      unsigned char bit12:1;
      unsigned char bit11:1;
      unsigned char bit10:1;
      unsigned char bit9:1;
      unsigned char bit8:1;
      unsigned char bit7:1;
      unsigned char bit6:1;
      unsigned char bit5:1;
      unsigned char bit4:1;
      unsigned char C3:1;
      unsigned char C2:1;
      unsigned char C1:1;
      unsigned char C0:1;
    }bits;

    struct
    {
      unsigned char byte0;
      unsigned char byte1;
      unsigned char byte2;
    }bytes;

}Registers;

Registers RDefault2[]={0x84,0,0x05,0x80,0,0x05} ;

Fehlermeldung:
pll.c:18: Warnung: geschweifte Klammern fehlen um Initialisierung
pll.c:18: Warnung: (nahe der Initialisierung für "RDefault2[0]"

Ausgabe des Unions ist:
Byte0: 132
Byte1: 0
Byte2: 0
Byte0: 5
Byte1: 128
Byte2: 0


MFG
Sebastian

von Karsten Brandt (Gast)


Lesenswert?

Bist Du Dir sicher, dass der dargestellte Quell-Code dem Original
entspricht?

Die Ausgabe ist nicht mit Deiner Initialisierung identisch!

Außerdem ist mir der Sinn Deiner Union noch nicht ganz klar.

Wie groß ist das größte Element Deiner Union?

3?

Dann würde ich die Initialisierung folgendermaßen schreiben:

Registers RDefault2[] = {{0x84, 0, 0x05}, {0x80, 0, 0x05}};

Karsten

von Sebastian B. (wastel)


Lesenswert?

Ja, das ist der Original Code. Meine Ausgabe stimmt eben nicht. Das
Größte Element ist 3, ja deine Schreibweise ist übersichtlicher, ändert
aber nicht die Problemstellung.
Für Abfrage benutze ich folgendes:
Registers *ptr;
ptr=RDefaults2;
Test=ptr->bytes.byte0;
usw.bis byte2
dann
ptr++;
und wieder das Gleiche.
Liegt hier vieleicht der Fehler??
MFG
Sebastian

P.S. Mir ist bewußt dass char all[2] dass gleiche wie struct bytes
macht. Ich wollte nur testen, ob das einen Unterschied bei meinem
Problem macht.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

>P.S. Mir ist bewußt dass char all[2] dass gleiche wie struct bytes
>macht.

Da ist schon mal ein Fehler drin: 'unsigned char all[2]' legt ein
Array aus 'char' mit der Feldgröße 2 an, 'struct bytes' enthält
aber derer drei.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

...aus Spaß an der Freude:

- main.c -
1
#include <stdio.h>
2
3
typedef union
4
{  
5
        unsigned char all[2];
6
  
7
        struct
8
        {
9
                unsigned char bit23:1;
10
                unsigned char bit22:1;
11
                unsigned char bit21:1;
12
                unsigned char bit20:1;
13
                unsigned char bit19:1;
14
                unsigned char bit18:1;
15
                unsigned char bit17:1;
16
                unsigned char bit16:1;
17
                unsigned char bit15:1;
18
                unsigned char bit14:1;
19
                unsigned char bit13:1;
20
                unsigned char bit12:1;
21
                unsigned char bit11:1;
22
                unsigned char bit10:1;
23
                unsigned char bit9:1;
24
                unsigned char bit8:1;
25
                unsigned char bit7:1;
26
                unsigned char bit6:1;
27
                unsigned char bit5:1;
28
                unsigned char bit4:1;
29
                unsigned char C3:1;
30
                unsigned char C2:1;
31
                unsigned char C1:1;
32
                unsigned char C0:1;
33
        }bits;
34
    
35
        struct
36
        {
37
                unsigned char byte0;
38
                unsigned char byte1;
39
                unsigned char byte2;
40
        }bytes;
41
      
42
}Registers;
43
44
Registers RDefault2[]={{0x84},{0},{0x05},{0x80},{0},{0x05}};
45
46
int
47
main(void)
48
{
49
50
        int i = 0;
51
52
        for(i = 0; i < 6; i++)
53
                printf("union #%d contains: "
54
                       "byte0=0x%02X byte1=0x%02X byte2=0x%02X\n",
55
                       i,
56
                       RDefault2[i].bytes.byte0,
57
                       RDefault2[i].bytes.byte1,
58
                       RDefault2[i].bytes.byte2);
59
60
        printf("Modifying member *.bits.C3\n");
61
62
        RDefault2[1].bits.C3 = 1;
63
        printf("union #1 contains: "
64
               "byte0=0x%02X byte1=0x%02X byte2=0x%02X\n",
65
               RDefault2[1].bytes.byte0,
66
               RDefault2[1].bytes.byte1,
67
               RDefault2[1].bytes.byte2);
68
        
69
        return 0;
70
}

Kompiliert mit:

 $ gcc -O -g -o union.exe main.c

Ergibt folgendes:

 $ union
 union #0 contains: byte0=0x84 byte1=0x00 byte2=0x00
 union #1 contains: byte0=0x00 byte1=0x00 byte2=0x00
 union #2 contains: byte0=0x05 byte1=0x00 byte2=0x00
 union #3 contains: byte0=0x80 byte1=0x00 byte2=0x00
 union #4 contains: byte0=0x00 byte1=0x00 byte2=0x00
 union #5 contains: byte0=0x05 byte1=0x00 byte2=0x00
 Modifying member *.bits.C3
 union #1 contains: byte0=0x00 byte1=0x00 byte2=0x10

von Fabian Scheler (Gast)


Lesenswert?

Desweiteren:

>    struct
>    {
>      unsigned char byte0;
>      unsigned char byte1;
>      unsigned char byte2;
>    }bytes;

das hier ist eine Struktur kein Bitfeld => du hast keine Garantie in
welcher Reihenfolge byte0, byte1 und byte2 tatsächlich im Speicher
liegen, die Wahrscheinlichkeit, dass sie genauso drin liegen, wie du es
hinschreibst, ist zwar hoch, aber ein anderer Compiler (oder eine andere
Compiler-Version) könnte das auch anders machen

> P.S. Mir ist bewußt dass char all[2] dass gleiche wie struct bytes
> macht. Ich wollte nur testen, ob das einen Unterschied bei meinem
> Problem macht.

du solltest aber nicht mit Quelltext experimentieren, der
offensichtlich falsch ist - meines Wissens sind solch ein union einfach
nicht definiert bzw. sollte dein Compiler meckern => dein Beispiel
müsste undefiniertes Verhalten erzeugen

> Registers RDefault2[]={0x84,0,0x05,0x80,0,0x05} ;

Kann man unions überhaupt so initialisieren? woher soll der Compiler
den Wissen, dass du die Darstellung als Struktur meinst? Funktioniert
das:

Registers RDefault2[2];
RDefault2[0].bytes = {0x84,0,0x05};
RDefault2[1].bytes = {0x80,0,0x05};

Ciao, Fabian

von Karl heinz B. (kbucheg)


Lesenswert?

> das hier ist eine Struktur kein Bitfeld => du hast keine Garantie in
> welcher Reihenfolge byte0, byte1 und byte2 tatsächlich im Speicher
> liegen,

Nicht nur das, auf manchen Systemen wird der Compiler
zwischen den einzelnen 'bytex' Membern auch noch zusätzliche
Padding Bytes einfügen um die Member wieder auf gerade
Adressen liegen zu haben.

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


Lesenswert?

> das hier ist eine Struktur kein Bitfeld => du hast keine Garantie in
> welcher Reihenfolge byte0, byte1 und byte2 tatsächlich im Speicher
> liegen, ...

Genau andersrum.  Bei einem Bitfeld gäbe es keine Garantie für die
Anordnung, aber nur in der Hinsicht, dass der Compiler die Elemente
entweder auf- oder absteigend sortieren darf -- wild ,,verwürfeln''
darf er nicht.  Bei einer Struktur ebenso: die Reihenfolge ist
garantiert, nicht jedoch (wie Karl Heinz anmerkt) ein mögliches
Auffüllen (Padding) zwischen den Elementen.

Die aufsteigende Reihenfolge der Anordnung der Elemente einer Struktur
im Speicher ist zwingend vorgeschrieben (6.7.2.1 Structure and union
specifiers, Absatz 13).

Zurück zur Initialisierung einer Union.  In ANSI-C89/ISO-C90 kann man
nur das erste benannte Element einer Union initialisieren (hier also
all[2]).  Da dieses selbst ein aggregate ist, benötigt es dabei
zusätzliche geschweifte Klammern:
1
Registers RDefault2[] = {
2
  { 0x84 },
3
  { 0 },
4
  { 0x05 },
5
  { 0x80 },
6
  { 0 },
7
  { 0x05}
8
};

Ist das (6 Unions insgesamt, nur das erste Byte jeweils explizit
initialisiert, der Rest implizit 0) eigentlich das, was du wolltest?
Oder wolltest du eher
1
Registers RDefault2[] = {
2
  { 0x84, 0 },
3
  { 0x05, 0x80 },
4
  { 0, 0x05 },
5
};

3 Unions insgesamt, jeweils beide Bytes von all[] initialisiert?

Anyway, ISO-C99 gibt uns hier deutlich mehr Freiheiten.  Jetzt geht
sowas:
1
Registers RDefault2[] = {
2
  [2] = {
3
    .all = {
4
      [0] = 0x84,
5
      [1] = 0x05,
6
    },
7
  },
8
  [0] = {
9
    .bytes = {
10
      .byte0 = 0x42,
11
      .byte2 = 0x05,
12
    },
13
  },
14
  [1] = {
15
    .bits = {
16
      .bit8 = 1,
17
      .bit11 = 1,
18
    },
19
  },
20
};

Man kann nur einen ,Zweig' der Union sinnvoll initialisieren, also
nur
entweder all, bytes oder bits.  Gibt man mehrere an, so ,,gewinnt''
die letzte Variante, nicht etwa eine Überlagerung.  Alle nicht
angegebenen Initialisierungswerte werden dabei als 0 angenommen.

von Fabian Scheler (Gast)


Lesenswert?

@Jörg: dankeschön!

von Sebastian B. (wastel)


Lesenswert?

Hallo,
danke für die vielen und schnellen Antworten ich werde sie sobald wie
mgl. testen.

>Da ist schon mal ein Fehler drin: 'unsigned char all[2]' legt ein
>Array aus 'char' mit der Feldgröße 2 an, 'struct bytes' enthält
>aber derer drei.

Richtig, ich müsste 'unsigned char all[3]' schreiben, mein Fehler!

Dank der sehr guten und detailierten Antwort von Jörg Wunsch erkennt
man nun auch warum meine Ausgabe 'fehlerhaft' war.

>Genau andersrum.  Bei einem Bitfeld gäbe es keine Garantie für die
>Anordnung, aber nur in der Hinsicht, dass der Compiler die Elemente
>entweder auf- oder absteigend sortieren darf -- wild ,,verwürfeln''
>darf er nicht.  Bei einer Struktur ebenso: die Reihenfolge ist
>garantiert, nicht jedoch (wie Karl Heinz anmerkt) ein mögliches
>Auffüllen (Padding) zwischen den Elementen.

Wie verhält sich der avr-gcc in dieser Sache?
Wo kann ich das nach lesen?
Begebe mich mal auf die Suche.

MFG
Sebastian

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


Lesenswert?

> Wie verhält sich der avr-gcc in dieser Sache?

Der AVR benötigt hardwaremäßig kein Padding (Ausnahme: Codezugriffe
im ROM, die müssen auf 16 Bits ausgerichtet sein, Datenzugriffe
jedoch nicht), insofern gibt's auch kein Padding innerhalb von
Strukturen.  Wenn der Code jedoch gleichzeitig mit dem GCC auf
einer anderen Architektur (z. B. PC) compiliert werden soll, muss
man vorsichtig sein.  Zwischen unsigned chars gibt es dort auch
kein Padding, jedoch zwischen einem unsigned char und einem Objekt,
das größer ist.  Das kann man mit __attribute__((packed)) verhindern.

__attribute__((packed)) kann man übrigens auch nehmen, um die Größe
eines enum von int auf den minimal notwendigen Platz einzuschränken.
Allerdings ist mir aufgefallen, dass zumindest der GDB damit ein
Problem hat, entweder bekommt er das in der Debug-Information nicht
mitgeliefert oder er macht selbst die Annahme, dass siezof(enum X)
== sizeof(int) sei.

von Sebastian B. (wastel)


Lesenswert?

Ein Blick in den Sourcecode der avr-libc, verrät auch warum Joerg sich
so gut auskennt.
;-)

MFG

Sebastian

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.