Nur ein Byte aus einem 32Bit-Wort herausziehen - wie am besten?

Gast #2132795
Lesenswert?

Hi zusammen!

Ich habe ein Datenwort mit 32Bit Länge - das möchte ich nun in Bytes 
umbasteln.

Hier gibt es ja mehrere Möglichkeiten, mit Pointer und ohne - der 
Einfachheit halber lieber ohne Pointer. Aber hier gehen ja auch noch 
verschiedene Sachen...

Also:
1
uint32_t data = 83749; // nur als Beispiel
2
uint8_t bytes[4];
3

4
// Variante 1:
5

6
bytes[0] = (uint8_t) (data >> 24);
7
bytes[1] = (uint8_t) (data >> 16);
8
bytes[2] = (uint8_t) (data >> 8);
9
bytes[3] = (uint8_t) data;
10

11
// Variante 2:
12

13
bytes[0] = (data >> 24) & 0xFF;
14
bytes[1] = (data >> 16) & 0xFF;
15
bytes[2] = (data >> 8) & 0xFF;
16
bytes[3] = data & 0xFF;
17

18
// geht auch sowas:
19

20
bytes[0] = (data & 0xFF000000);
21
bytes[1] = (data & 0xFF0000);
22
bytes[2] = (data & 0xFF00);
23
bytes[3] = data & 0xFF;

Oder gibt es eine noch bessere Möglichkeit?
#2132813
Lesenswert?

Das funktioniert jedenfalls nicht !
1
/* FALSCH ! */
2
bytes[0] = (data & 0xFF000000);
3
bytes[1] = (data & 0xFF0000);
4
bytes[2] = (data & 0xFF00);
Müsste so aussehen:
1
bytes[0] = (data & 0xFF000000) >> 24;
2
bytes[1] = (data & 0xFF0000) >> 16;
3
bytes[2] = (data & 0xFF00) >> 8;

Und dann gibt es da noch die "Edianness" zu berücksichtigen.
http://en.wikipedia.org/wiki/Endianness
bzw.
http://de.wikipedia.org/wiki/Endianness
Gast #2132933
Lesenswert?

OK, vielen Dank schonmal!

Ist denn zwischen den beiden
1
// Variante 1:
2

3
bytes[0] = (uint8_t) (data >> 24);
4
bytes[1] = (uint8_t) (data >> 16);
5
bytes[2] = (uint8_t) (data >> 8);
6
bytes[3] = (uint8_t) data;
7

8
// Variante 2:
9

10
bytes[0] = (data >> 24) & 0xFF;
11
bytes[1] = (data >> 16) & 0xFF;
12
bytes[2] = (data >> 8) & 0xFF;
13
bytes[3] = data & 0xFF;
ein Unterschied, weshalb das eine dem anderen vorziehen sollte?
#2132939
Lesenswert?

Was spricht gegen:
1
bytes[0] = data >> 24;
2
bytes[1] = data >> 16;
3
bytes[2] = data >> 8;
4
bytes[3] = data;
wenn man mal davon ausgeht, dass ein Byte 8 Bits hat?

Ein nicht zu dämlicher Compiler wird daraus Code erzeugen, der nicht 
schlechter ist als der Weg über eine Union, oft hingegen besser.
Gast #2132984
Lesenswert?

Hans schrieb:
> OK, vielen Dank schonmal!
>
> Ist denn zwischen den beiden// Variante 1:
>
> bytes[0] = (uint8_t) (data >> 24);
> bytes[1] = (uint8_t) (data >> 16);
> bytes[2] = (uint8_t) (data >> 8);
> bytes[3] = (uint8_t) data;
>
> // Variante 2:
>
> bytes[0] = (data >> 24) & 0xFF;
> bytes[1] = (data >> 16) & 0xFF;
> bytes[2] = (data >> 8) & 0xFF;
> bytes[3] = data & 0xFF;
> ein Unterschied, weshalb das eine dem anderen vorziehen sollte?

Ich hatte mit einem DSP zu tun der mit 16-Bit je Adresse gearbeitet hat. 
Dadurch ist 16-Bit die kleinste addressierbare Einheit. Ein unsigned 
char war zwar sizeof(unsigned char)=1 aber mit 16-Bit Wertebereich. 
Variante 1 hätte auf dieser Plattform nicht das gewünschte Ergebnis 
geliefert (ich habe stundenlang nach dem Fehler gesucht: Der Cast nach 
unsigned char hat hier 16-Bit stehen lassen).

Ich persönlich nutze seither immer Variante 2, auch wenn ich der Meinung 
bin das das beobachtete Verhalten mehr ein Compilerproblem war...
#2133024
Lesenswert?

Es gab/gibt eine AVR-GCC Version (fragt bitte nicht welche) die
1
bytes = (ulong & 0xFF000000UL) >> 24;
in ein elegantes "move byte from register to register" übersetzt hat, 
und
1
bytes = ulong >> 24;
in eine Schleife mit 24 shift Operationen quer durch vier Bytes.

Darum habe ich mir das gemerkt und prüfe (fast) immer was der Compiler 
aus der jeweiligen Version macht.

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