Union zum casten nutzen?

Gast #5284383
Lesenswert?

Hallo,

über der seriellen Schnittstelle wird 8bit Datenkette versendet,
die als 10bit Zeichenkette interpretiert werden soll.

aus : abcdefgh ijklmnop qrstuvwx ....
wird: abcdefghij klmnopqrst uvwx....

meine Lösung um ohne bitshifting die Variablen umzuwandeln:
1
typedef struct {
2
  uint16_t b0 : 10;
3
  uint16_t b1 : 10;
4
  uint16_t b2 : 10;
5
  uint16_t b3 : 10;
6
} uint10b_t;
7

8
typedef union {
9
  uint10b_t data10;
10
  uint8_t data8[5];
11
} data_u;
12

13
int main(void)
14
{
15
  data_u data;
16
  data.data8[0] = 255;
17
  data.data8[1] = 255;
18
  data.data10.b0; // enthält 1023
19
  data.data10.b1; // enthält 63
20
}

Soweit läuft es auch wie gewollt.
Nachteil ist jedoch dass man die 10bit Variablen nicht in einer
Schleife sondern einzeln bearbeiten muss, da man kein Bit-Array
erstellen kann, und ich deshalb den Umweg mittels struct gehe.

Als ich mich genauer damit befasst habe, habe ich festgestellt,
dass dieser Weg nicht elegant ist, da das alignment vom Compiler
nicht garantiert wird.

Ist das "unleserliche" bit-shifting der einzige Weg, der wirklich
zulässig ist, oder gibt es noch andere Lösungen um sowas zu casten?
Gast #5284401
Lesenswert?

Ja klar mach der Compiler das im Endeffekt per bitshifting,
nur mit dem Unterschied, dass man den Code besser lesen kann.

Es kann jedoch passieren, dass ein anderer Compiler die 10bits
in einem uint16_t packt und nicht mehr shiftet.

Deshalb mein Posting, vielleicht hat jemand einen anderen Ansatz.
Gast #5284442
Lesenswert?

Normalerweise gibt es bitfelder nur gepackt in Einheiten des verwendeten 
Typs.

Du hast einen 16 bit-typ, dann sollten darin 3 5-bit-felder möglich 
sein, aber nur ein 10bit Feld. Zum nächsten sollt 6bit frei bleiben
Gast #5284600
Lesenswert?

Ma W. schrieb:
> Typumwandlung mit union ist Undefined Behavior.

Nicht seit C99 in C.

> Man darf nicht in ein Union-Feld etwas schreiben und es aus einem
> anderen Union-Feld rücklesen.

Doch, darf man. Type-punning per Union ist erlaubt. Im Zusammenhang mit 
Bitfeldern ist das Ergebnis dann allerdings implementationsabhängig, 
weil nicht spezifiziert ist, wie herum die Bits eingepackt werden.

Wenn man portablen Code haben will, sollte man Bitfelder ausschließlich 
zum Laden und Speichern nutzen.
(Firma: RUB) #5284626
Lesenswert?

Nop schrieb:
> Ma W. schrieb:
>> Typumwandlung mit union ist Undefined Behavior.
>
> Nicht seit C99 in C.
>
>> Man darf nicht in ein Union-Feld etwas schreiben und es aus einem
>> anderen Union-Feld rücklesen.
>
> Doch, darf man. Type-punning per Union ist erlaubt.
Da wir nicht wissen, ob der TO C oder C++ verwendet, ist die Frage nach 
dem "UB or not UB" etwas muessig.

https://stackoverflow.com/questions/11373203/accessing-inactive-union-member-and-undefined-behavior/11996970#11996970
1
The confusion is that C explicitly permits type-punning through a union,
2
whereas C++ (C++11) has no such permission.
Gast #5284824
Lesenswert?

p41145 schrieb:

> Also sehe ich es richtig, es ist nicht zulässig,

Doch, ist es in C99 und später. gnu99 setzt auf C99 nur noch ein paar 
Erweiterungen drauf.

Aber es ist nicht portabel, weil nicht festgelegt ist, wie herum der 
Compiler die Bitfields implementiert - MSB oben oder unten und Endianess 
seien genannt. Deswegen sollte man Bitfields für sowas nicht verwenden, 
sondern das mit richtigen Umwandlungen machen.
#5284881
Lesenswert?

p41145 schrieb:
> Nachteil ist jedoch dass man die 10bit Variablen nicht in einer
> Schleife sondern einzeln bearbeiten muss

Und man darf sich auch nicht im Listing anschauen, welch riesen Bohei 
der Compiler für Bitfelder veranstalten muß.

Ich würds einfach mit ner Funktion machen, dann geht auch ne Schleife.
1
uint16_t get10bit( uint8_t *p, uint8_t i )
2
{
3
  return *(uint16_t *)(p+i) >> i*2 & 0x03FF;
4
}

P.S.:
Der Code ist für Little-Endian.
Gast #5285104
Lesenswert?

Bernd K. schrieb:
> Bist Du sicher daß der Code das macht was gefordert ist?

Ich kann zumindest bestätigen, dass es bis zu 5x8bit zu
4x10bit gut funktioniert. Man muss den Pointer alle vier
10bits nochmal um ein byte versetzen dann klappt es auch
für größere Arrays.

@Peter Dannegger:
Die Priorität der Operatoren finde ich nicht trivial,
schwer so zu lesen, aber ein guter Ansatz.
1
uint16_t get10bit( uint8_t *p, uint8_t i ){
2
  uint16_t * ptr;
3
  ptr = (uint16_t *) (p + i + (i / 4));
4
  return  (*ptr >> ((i&3)*2)) & 0x03FF;
5
}
6

7
void put10bit( uint8_t *p, uint8_t i, uint16_t data){
8
  uint16_t * ptr;
9
  uint16_t mask = 0x3FF;
10
  uint8_t shift = ((i & 3) * 2);
11
  ptr = (uint16_t *) (p + i + (i / 4));
12
  mask <<= shift;
13
  data <<= shift;
14
  *ptr &= ~mask;
15
  *ptr |= data;
16
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren