Forum: Compiler & IDEs Unterschiedliche Structs aufeinander abbilden


von Tim (Gast)


Lesenswert?

N'Abend,

mal angenommen man hat zwei Structs, in denen meinentwegen jeweils ein 
uint8_t steckt, das in Bildfelder unterteilt ist um auf diese Art 
Statusflags zu verwalten. Und beide Structs enthalten 4 von 8 Bits, die 
vom einen in das andere Struct sollen, dann kann man das natürlich mit 
Zuweisungen á la
1
structa.bitxyz=structb.bitxyz
 machen. Das ganze dann für jedes Bit und fertig ist die Sache. Aber 
geht es auch eleganter?

Wenn ich ein uint8_t als Zwischenstation nutzen und ihm den Inhalt von 
structa zuweise, eine passende Bitmaske "drüberunde" und das Resultat 
dann in structb schreibe, wäre das doch netter. Noch besser wär's 
natürlich ohne diesen Zwischenschritt. Aber da ja beide Struct 
unterschiedlichen Typs sind, klappt die direkte Zuweisung nicht.

Kann man, wenn man genau weiß dass beide die gleiche Größe im Speicher 
haben, nicht dafür sorgen, dass man so ne Art
1
structb=structa&0xF0
 compiliert bekommen?

Grüße

von Noname (Gast)


Lesenswert?

Praktisch geht das (oft). Theoretisch nicht.

Praktisch etwa, in dem man in einer union die beiden Strukturen 
definiert und zwar so, das die Annahmen:
1. Elemente von structs werden in der Reihenfolge angeordnet, wie sie 
geschrieben sind
2. Datentypen gleicher Grösse auf der Maschine belegen den selben 
Speicherplatz.
3. Abschnitte in den den Datentypen ungleicher Grösse definiert sind und 
deren Länge sich numerisch gleicht, enden an der selben Speicheradresse.

gelten und gegebenfalls das pragma "pack" verwendet wird.
Das pragma pack sorgt zwangsweise dafür, das die Annahme 3 gilt.

Theoretisch nicht. Denn die Annahme 1 ist vom Sprachstandard nicht 
zugesichert. Und die Annahme 3. ist nur mit dem pragma pack gültig. Denn 
dem Compiler steht es sonst frei, etwa für einen effektiven Zugriff 
beliebe Pad-Bytes einzufügen. Nicht alle (aber die meisten modernen) 
Compiler bieten diese pragma.

Einzig Annahme 2. kann als gesichert betrachtet werden, denn die Länge 
eines Typs ist immer gleich.

Das Problem ist also eigentlich nur Annahme 1. Praktisch wird die 
Struktur immer in der Reihenfolge des Programmtextes aufgebaut. Aber 
verlassen kann man sich nicht darauf. Das Programm muss also bei einer 
Portierung immer daraufhin getestet werden.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Noname schrieb:
> Praktisch wird die Struktur immer in der Reihenfolge des Programmtextes
> aufgebaut. Aber verlassen kann man sich nicht darauf.

Doch, wird in Absatz 15 des C LRM beschrieben: 6.7.2.1 "Structure and 
union specifiers".

Man kann die beiden structs als union definieren, so dass jede Struktur 
auch durch einen integer Typ definiert ist, oder besser als weiteres 
bitfield, dass alle vier bits enthält. Hier musst Du allerdings 
aufpassen, wie das vom ABI des Prozessors genau definiert wird. Dann 
kann auf die einzelnen bits wie gehabt zugegriffen werden, und die 
Zuweisung geht auch von alleine.
Letzteres widerspricht nun tatsächlich dem C Standard, wird aber von den 
wichtigsten Compilern korrekt umgesetzt.
1
union {
2
  struct {
3
    uint8_t a : 1;
4
    uint8_t b : 1;
5
    uint8_t c : 1;
6
    uint8_t d : 1;
7
    uint8_t   : 4;
8
  } bits;
9
  struct {
10
    uint8_t n : 4;
11
    uint8_t   : 4;
12
  } nybble;
13
} a;
14
15
union {
16
  struct {
17
    uint8_t   : 2;
18
    uint8_t a : 1;
19
    uint8_t b : 1;
20
    uint8_t c : 1;
21
    uint8_t d : 1;
22
    uint8_t   : 2;
23
  } bits;
24
  struct {
25
    uint8_t   : 2;
26
    uint8_t n : 4;
27
    uint8_t   : 2;
28
  } nybble;
29
} b;
30
31
a.bits.b = 1;
32
b.nybble.n = a.nybble.n;
33
if (b.bits.b == 1) { puts("Yay!");}

von Stefan E. (sternst)


Lesenswert?

Marcus Harnisch schrieb:
> Doch, wird in Absatz 15 des C LRM beschrieben: 6.7.2.1 "Structure and
> union specifiers".

Siehst du den gelöschten Beitrag vor deinem? Ich hatte ähnliches 
geschrieben, und habe erst danach gemerkt, dass es ja um ein reines 
Bit-Feld geht. Und die Reihenfolge der Elemente innerhalb eines 
Bit-Feldes ist eben nicht garantiert.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:
> und habe erst danach gemerkt, dass es ja um ein reines
> Bit-Feld geht. Und die Reihenfolge der Elemente innerhalb eines
> Bit-Feldes ist eben nicht garantiert.

Schon, das ist aber im allgemeinen durch das ABI des Prozessors 
festgelegt. Soll der Code architekturunabhängig sein, dann muss man das 
eben für die zu unterstützenden Plattformen spezifisch umsetzen und per 
Schalter die geeignete Version aktivieren.

von Stefan E. (sternst)


Lesenswert?

Marcus Harnisch schrieb:
> Schon, das ist aber im allgemeinen durch das ABI des Prozessors
> festgelegt.

Hä? Was meinst du damit?
Bei einem Bit-Feld steht es dem Compiler komplett frei, die Reihenfolge 
nach Belieben zu ändern.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:
> Marcus Harnisch schrieb:
>> Schon, das ist aber im allgemeinen durch das ABI des Prozessors
>> festgelegt.
>
> Hä? Was meinst du damit?
> Bei einem Bit-Feld steht es dem Compiler komplett frei, die Reihenfolge
> nach Belieben zu ändern.

Richtig, das LRM lässt das offen (implementation defined, bzw. 
unspecified).
Theoretisch könnte der Compiler das durchaus für jede Struktur anders 
machen.
Da aber soviel Anarchie in diesem Zusammenhang nicht sinnvoll ist, man 
will ja zumindest unterschiedlich übersetzte .o linken können, gibt es 
für verschiedene Architekturen eben ein ABI, dass derartige Dinge 
spezifiziert. Für ARM kann man dass z.B. hier im AAPCS nachlesen:
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/index.html

von Tim (Gast)


Lesenswert?

Danke für die interessanten Beiträge. Scheint also tatsächlich nicht mit 
einfachen Standardmitteln möglich und auch nicht ganz ungefährlich zu 
sein. Ich werde in der Richtung mal ein paar Tests machen :).

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


Lesenswert?

Marcus Harnisch schrieb:
> Richtig, das LRM lässt das offen (implementation defined, bzw.
> unspecified).

Nicht ganz.  Erstens schreibt der Standard vor, dass aufeinander-
folgende Felder auch aufeinanderfolgend zuzuteilen sind.  Zweitens
heißt "implementation defined", dass der Compiler dokumentieren
muss, in welcher Reihenfolge er dies tut.  Sollte er für
verschiedene Szenarien wirklich verschiedene Reihenfolgen wählen,
dann müsste er dies (mit den auslösenden Umständen) also ebenfalls
dokumentieren.

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.