Hallo liebes Forum, wie kann ich uint32_t Big Endian nach little Endian konvertieren? Meine Lösung hat jede Menge Schiebereien und Masken. Ich schäme mich dafür. Gibt es keine elegante Lösung?
Ach du dicker Vater schrieb: > uint32_t Du kannst auch mit einem passenden Zeiger aus diesem 4-Bytes-Speicherblock jedes Byte einzeln rauspicken und ausgeben/irgenwo ablegen.
1 | uint32_t num=0x1f2f3f4f; |
2 | printf("Zahl: %" PRIx32 "\n\n", num); |
3 | |
4 | const size_t ARR_LEN=sizeof(num); |
5 | |
6 | for(size_t i=0; i<ARR_LEN; i++){ |
7 | printf("%" PRIx8 "\t", *((uint8_t*)&num+i)); |
8 | }
|
9 | |
10 | puts(""); |
11 | |
12 | for(size_t i=0; i<ARR_LEN; i++){ |
13 | printf("%" PRIx8 "\t", *((uint8_t*)&num+(ARR_LEN-1)-i)); |
14 | }
|
1 | Zahl: 1f2f3f4f |
2 | |
3 | 4f 3f 2f 1f |
4 | 1f 2f 3f 4f |
Ach du dicker Vater schrieb: > Hallo liebes Forum, wie kann ich uint32_t Big Endian nach little Endian > konvertieren? Meine Lösung hat jede Menge Schiebereien und Masken. Kommt drauf an, was du unter "jede Menge" verstehst, aber klingt erstmal nicht falsch. > Ich schäme mich dafür. Warum? > Gibt es keine elegante Lösung? Was macht eine Lösung für dich elegant?
Bit Shifts sind hier schon richtig. Deren Funktionsweise ist, im Gegensatz zu Pointer Spielchen und "union", garantiert. Siehe auch Serialisierung.
Ach du dicker Vater schrieb: > Meine Lösung hat jede Menge Schiebereien und Masken. Ich > schäme mich dafür. Gibt es keine elegante Lösung? Schreib es so hin, dass es leicht zu lesen ist. Wenn der Compiler optimieren darf, wird er das erkennen.
Ach du dicker Vater schrieb: > wie kann ich uint32_t Big Endian nach little Endian konvertieren? Mit GCC: __builtin_bswap32 (var)
Johann L. schrieb: > Ach du dicker Vater schrieb: >> wie kann ich uint32_t Big Endian nach little Endian konvertieren? > > Mit GCC: __builtin_bswap32 (var) Viel zu einfach/funktionierend! ;-)
Ich mach's so (vor Ewigkeiten irgendwo gefunden):
1 | #define SWAP16(x) { register UINT8 aux8; \
|
2 | aux8 = (UINT8) ((x) >> 8); \
|
3 | (x) = ((x) << 8) | aux8; }
|
4 | |
5 | #define SWAP32(x) { register UINT32 aux32; \
|
6 | aux32 = (x); \
|
7 | (x) = (aux32 >> 24 & 0xFF) | (aux32 >> 8 & 0xFF00); \
|
8 | (x)|= (aux32 << 24 & 0xFF000000) | (aux32 << 8 & 0xFF0000);}
|
Rolf M. schrieb: > Ach du dicker Vater schrieb: >> Hallo liebes Forum, wie kann ich uint32_t Big Endian nach little Endian >> konvertieren? Meine Lösung hat jede Menge Schiebereien und Masken. > > Kommt drauf an, was du unter "jede Menge" verstehst, aber klingt erstmal > nicht falsch. Für zwei Vertauschungsoperationen von je zwei Bytes klingt "jede Menge Schiebereien und Masken" nicht nach dem direktesten Weg. Welche Wortbreite hat der Prozessor?
my2ct schrieb: > Für zwei Vertauschungsoperationen von je zwei Bytes klingt "jede Menge > Schiebereien und Masken" nicht nach dem direktesten Weg. Naja, pro Byte einmal schieben und maskieren halt. Daher ja meine Frage, was der "dicke Vater" unter "jede Menge" versteht.
Rolf M. schrieb: > Naja, pro Byte einmal schieben und maskieren halt. Das muß am Ende nicht unbedingt das Ergebnis sein. Compiler erkennen z.B., wenn rotiert werden soll. Auch wenn's in C kein Statement gibt, um diesen Wunsch direkt hinzuschreiben, wird - so denn die Plattform dafür einen Maschinenbefehl hat - im Code aus der Maskier-/Schieberei eine Rotate-Instruktion. Compiler, die schlau genug sind, können genauso gut aus der entsprechenden Schiebe- und Maskierkombination eine BSWAP-Instruktion basteln (wenn es die auf der Zielplattform gibt).
Markus F. schrieb: > Rolf M. schrieb: >> Naja, pro Byte einmal schieben und maskieren halt. > > Das muß am Ende nicht unbedingt das Ergebnis sein. Es ging hier darum, wie der Code aussieht, nicht darum, was nachher der Compiler draus macht.
Für eine portable Lösung sollte man die Funktionen aus <netinet/in.h> verwenden. Andernfalls handelt man sich Ärger ein, wenn das Programm mal auf einer Bigendian Architektur übersetzt wird. https://beej.us/guide/bgnet/html/multi/htonsman.html
"• REV: Converts either: – 32-bit big-endian data into little-endian data – or 32-bit little-endian data into big-endian data. • REV16: Converts either: – 16-bit big-endian data into little-endian data – or 16-bit little-endian data into big-endian data. • REVSH: Converts either: – 16-bit signed big-endian data into 32-bit signed little-endian data – 16-bit signed little-endian data into 32-bit signed big-endian data" #asm(" REV r0,r0"); #asm(" bx lr"); Und aus die Maus.
PostalDude schrieb: > #asm(" REV r0,r0"); > #asm(" bx lr"); > > Und aus die Maus. Und für welche Prozessor-Architektur gilt das überhaupt?
MikeH schrieb: > Für eine portable Lösung sollte man die Funktionen aus <netinet/in.h> > verwenden Auch nicht so richtig portabel, da nur bei POSIX vorhanden. Bitweise Operationen hingegen gibt es immer.
Rolf M. schrieb: > Und für welche Prozessor-Architektur gilt das überhaupt? Ziemlich sicher ARM. Aber #asm("bx lr") ist so ziemlich das dümmste, was man machen kann. Die sinnvollste Lösung ist wahrscheinlich das genannte Compiler-Builtin, denn das ist garantiert optimal implementiert.
Unbedingt mit den Funktionen: htons(), htonl(), ntohs(), ntohl() => wurde schon mal genannt. https://beej.us/guide/bgnet/html/multi/htonsman.html Die Funktionen machen nur was, wo nötig und je nach Compiler/Lib auch schön optimiert...
Mike schrieb: > Unbedingt mit den Funktionen: htons(), htonl(), ntohs(), ntohl() Was ist der Vorteil gegenüber Bitshifts? Mike schrieb: > Die Funktionen machen nur was, wo nötig und je nach Compiler/Lib auch > schön optimiert... Bitshifts werden genau so vom Compiler optimiert. Außerdem gibt es die auch auf Nicht-Posix-Systemen.
Niklas Gürtler schrieb: > Was ist der Vorteil gegenüber Bitshifts? Und was ist der Nachteil gegenüber Bitshifts: auf einem Big-Endian System machen diese Funktionen genau nullkommanix. Wenn man das haben will (network byte order): gut. Wenn nicht, halt eher nicht...
Mike schrieb: > Unbedingt mit den Funktionen: htons(), htonl(), ntohs(), ntohl() Ach du dicker Vater schrieb: > wie kann ich uint32_t Big Endian nach little Endian konvertieren? htonl() wäre dann richtig, wenn der TE geschrieben hätte: "wie kann ich uint32_t von der internen Darstellung nach der Network- Byte-Order konvertieren?" Da die interne Darstellung nicht unbedingt Big-Endian und die Network- Byte-Order nicht unbedingt Little-Endian sein muss, ist hier nach einer Funktion gefragt, die die Byte-Reihenfolge auf jeden Fall umkehrt.
@Yalu X >htonl() wäre dann richtig, wenn der TE geschrieben hätte: > wie kann ich uint32_t von der internen Darstellung nach der Network- > Byte-Order konvertieren?" Ob der Host-Rechner (CPU) mit Big-Endian oder Little-Endian funzt, hängt von der Archidektur ab. Die Network-Byte-Order ist aber IMMER Big-Endian: Ergo mit dem 'htonl' Makro hat man unabhängig vom Host (portable) eine definierte Byteorder.
Mike schrieb: > Die Network-Byte-Order ist aber IMMER > Big-Endian: Ergo mit dem 'htonl' Makro hat man unabhängig vom Host > (portable) eine definierte Byteorder. Es gibt auch Netzwerk-Protokolle mit Little Endian, z.B. CANopen. Mit Bitshifts kann man ganz genauso eine portable definierte Byte-Order erreichen, z.B.:
1 | uint16_t parse (const uint8_t* data, size_t length) { |
2 | assert (length >= 2); |
3 | return data [0] | (((uint16_t) data [1]) << 8); |
4 | }
|
Gibt unabhängig von der Host-Order die ersten beiden Bytes des Puffers als Little Endian interpretiert zurück. Allerdings war die Frage des TO überhaupt nicht, von einer bestimmten Reihenfolge auf die Host-Reihenfolge umzuwandeln, sondern lediglich, die Reihenfolge unabhängig vom Host zu verdrehen.
Niklas Gürtler schrieb: > Es gibt auch Netzwerk-Protokolle mit Little Endian, z.B. CANopen. Das mag zwar durchaus so sein, aber htonl usw. stammen aber definitiv aus dem POSIX-API. Und selbiges umfasst primär die IP-basierte Kommunikation auf UNIX-artigen Systemen. Auch wenn es vermutlich eher zur Polumkehrung des Erdmagnetfeldes kommen mag als zur Änderung der Network Byte Order und somit zur Änderung des Verhaltens von htonl usw., finde ich es aber auch semantisch ungünstig, auf ein Netzwerk-API zu referenzieren, wenn es überhaupt nicht um die Kommunikation mittels dieses API geht. Leider betreibt der TE aber natürlich die heute übliche Salamitaktik und nennt nicht etwa den Kontext, in dem er die Änderung der Endianess durchführen will/muss. Generell halte ich es aber aber schon für sehr sinnvoll, die Anpassung der Endianess einer Bibliotheksfunktion zu überlassen, für die es ggf. je nach Prozessorarchitektur optimierte Implementierungen gibt.
Mike schrieb: > @Yalu X >>htonl() wäre dann richtig, wenn der TE geschrieben hätte: >> wie kann ich uint32_t von der internen Darstellung nach der Network- >> Byte-Order konvertieren?" > > Ob der Host-Rechner (CPU) mit Big-Endian oder Little-Endian funzt, hängt > von der Archidektur ab. Das ist schon klar. Leider meldet sich der TE nicht mehr, um uns mitzuteilen, was er mit der Konvertierung tatsächlich bezwecken möchte. So bleibt uns als einzige Information, dass er ein uint32_t von Big-Endian nach Little-Endian konvertieren möchte. Woher das zu konvertierende uint32_t kommt, und wohin das konvertierte uint32_t geht, bleibt dabei völlig im Dunkeln. Unter den gegebenen Umständen muss man davon ausgehen, dass der Input immer Big-Endian und der Output immer Little-Endian ist. Deswegen ist ein Byte-Swap (egal, auf welche Weise realisiert), der einzig richtige Vorschlag.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.