Forum: Compiler & IDEs Big Endian 2 little Endian


von Ach du dicker Vater (Gast)


Lesenswert?

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?

von Musl (Gast)


Lesenswert?


von zitter_ned_aso (Gast)


Lesenswert?

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

von Andreas R. (daybyter)


Lesenswert?


von Rolf M. (rmagnus)


Lesenswert?

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?

von Niklas Gürtler (Gast)


Lesenswert?

Bit Shifts sind hier schon richtig. Deren Funktionsweise ist, im 
Gegensatz zu Pointer Spielchen und "union", garantiert. Siehe auch 
Serialisierung.

von Dirk B. (dirkb2)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ach du dicker Vater schrieb:
> wie kann ich uint32_t Big Endian nach little Endian konvertieren?

Mit GCC: __builtin_bswap32 (var)

von NichtWichtig (Gast)


Lesenswert?

Hier gib es auch noch Lesestoiff, Lösungen und Beispiele

https://linux.die.net/man/3/endian

von Carl D. (jcw2)


Lesenswert?

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!

;-)

von marais (Gast)


Lesenswert?

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);}

von my2ct (Gast)


Lesenswert?

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?

von Rolf M. (rmagnus)


Lesenswert?

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.

von Markus F. (mfro)


Lesenswert?

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).

von Rolf M. (rmagnus)


Lesenswert?

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.

von MikeH (Gast)


Lesenswert?

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

von PostalDude (Gast)


Lesenswert?

"• 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.

von Rolf M. (rmagnus)


Lesenswert?

PostalDude schrieb:
> #asm(" REV r0,r0");
> #asm(" bx lr");
>
> Und aus die Maus.

Und für welche Prozessor-Architektur gilt das überhaupt?

von Niklas Gürtler (Gast)


Lesenswert?

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.

von S. R. (svenska)


Lesenswert?

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.

von Mike (Gast)


Lesenswert?

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...

von Niklas Gürtler (Gast)


Lesenswert?

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.

von Markus F. (mfro)


Lesenswert?

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...

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Mike (Gast)


Lesenswert?

@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.

von Niklas Gürtler (Gast)


Lesenswert?

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.

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

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
Noch kein Account? Hier anmelden.