Ich muss eine Funktion schreiben, um Big Endian in Little Endian in C zu konvertieren. Ich kann keine Bibliotheksfunktion verwenden.
c
swap
endianness
Alex Xander
quelle
quelle
Antworten:
Angenommen, Sie benötigen einen einfachen Byte-Austausch, versuchen Sie etwas wie
16-Bit-Konvertierung ohne Vorzeichen:
32-Bit-Konvertierung ohne Vorzeichen:
Dies vertauscht die Bytereihenfolgen von den Positionen 1234 auf 4321. Wenn Ihre Eingabe war
0xdeadbeef
, könnte ein 32-Bit-Endian-Swap eine Ausgabe von haben0xefbeadde
.Der obige Code sollte mit Makros oder zumindest Konstanten anstelle von magischen Zahlen bereinigt werden, aber hoffentlich hilft er so wie er ist
BEARBEITEN: Wie eine andere Antwort hervorhob, gibt es plattform-, betriebssystem- und befehlssatzspezifische Alternativen, die VIEL schneller sein können als die oben genannten. Im Linux-Kernel gibt es Makros (z. B. cpu_to_be32), die mit Endianness ziemlich gut umgehen. Diese Alternativen sind jedoch spezifisch für ihre Umgebung. In der Praxis wird Endianness am besten mit einer Mischung verfügbarer Ansätze behandelt
quelle
((num & 0xff) >> 8) | (num << 8)
, generiert gcc 4.8.3 einen einzelnenrol
Befehl. Wenn die 32-Bit-Konvertierung als geschrieben wird((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)
, generiert derselbe Compiler einen einzelnenbswap
Befehl.struct byte_t reverse(struct byte_t b) { struct byte_t rev; rev.ba = b.bh; rev.bb = b.bg; rev.bc = b.bf; rev.bd = b.be; rev.be = b.bd; rev.bf = b.bc; rev.bg = b.bb; rev.bh = b.ba; return rev;}
wobei dies ein Bitfeld mit 8 Feldern mit jeweils 1 Bit ist. Aber ich bin mir nicht sicher, ob das so schnell geht wie die anderen Vorschläge. Verwenden Sie für Ints die Optionunion { int i; byte_t[sizeof(int)]; }
, Byte für Byte in der Ganzzahl umzukehren.Durch Einbeziehung:
Sie können eine optimierte Version der maschinenabhängigen Byte-Swapping-Funktionen erhalten. Dann können Sie einfach die folgenden Funktionen verwenden:
oder
quelle
#include <byteswap.h>
, siehe Kommentar in der .h-Datei selbst. Dieser Beitrag enthält hilfreiche Informationen, daher habe ich abgestimmt, obwohl der Autor die OP-Anforderung ignoriert hat, keine lib-Funktion zu verwenden.Update : 64-Bit-Byte-Austausch hinzugefügt
quelle
int32_t
undint64_t
-Varianten die Begründung für die Maskierung von... & 0xFFFF
und... & 0xFFFFFFFFULL
? Ist hier etwas mit Zeichenerweiterung los, das ich nicht sehe? Warumswap_int64
kehrt man zurückuint64_t
? Sollte das nicht seinint64_t
?swap_int64
in Ihrer Antwort ändern . +1 für die hilfreiche Antwort, übrigens!LL
sind unnötig,(u)swap_uint64()
ähnlich wie einL
nicht benötigt wird(u)swap_uint32()
. DasU
wird nicht benötigt,uswap_uint64()
ähnlich wie dasU
nicht benötigt wird inuswap_uint32()
Hier ist eine ziemlich allgemeine Version; Ich habe es nicht kompiliert, daher gibt es wahrscheinlich Tippfehler, aber Sie sollten auf die Idee kommen,
NB: Dies ist nicht für Geschwindigkeit oder Platz optimiert. Es soll klar (leicht zu debuggen) und portabel sein.
Update 2018-04-04 Es wurde assert () hinzugefügt, um den ungültigen Fall von n == 0 abzufangen, wie vom Kommentator @chux festgestellt.
quelle
bswap
von einem anständigen X86-Compiler mit aktivierter Optimierung zu einem einzigen Befehl kompiliert . Diese Version mit einem Parameter für die Größe konnte das nicht.Wenn Sie Makros benötigen (z. B. eingebettetes System):
quelle
UINT
in ihrem Namen.Bearbeiten: Dies sind Bibliotheksfunktionen. Das Befolgen dieser Anweisungen erfolgt manuell.
Ich bin absolut verblüfft über die Anzahl der Personen, die __byteswap_ushort, __byteswap_ulong und __byteswap_uint64 nicht kennen . Sicher, sie sind Visual C ++ -spezifisch, aber sie kompilieren auf x86 / IA-64-Architekturen bis auf köstlichen Code. :) :)
Hier ist eine explizite Verwendung der
bswap
Anweisung, die von dieser Seite abgerufen wurde . Beachten Sie, dass die oben angegebene intrinsische Form immer schneller ist. Ich habe sie nur hinzugefügt, um eine Antwort ohne Bibliotheksroutine zu geben.quelle
Als Witz:
quelle
int i, size_t sizeofInt
und nicht der gleiche Typ für beide.Hier ist eine Möglichkeit, den SSSE3-Befehl pshufb unter Verwendung seiner Intel-Eigenschaft zu verwenden, vorausgesetzt, Sie haben ein Vielfaches von 4
int
s:quelle
Wird das schneller funktionieren / sein?
quelle
char
nichtbyte
.Hier ist eine Funktion, die ich verwendet habe - getestet und funktioniert mit jedem grundlegenden Datentyp:
quelle
source
basiert auf einer sehr vernünftigen Annahme: Er wird nach Bedarf ausgerichtet. Wenn diese Annahme jedoch nicht zutrifft, lautet der Code UB.BEARBEITEN: Diese Funktion tauscht nur die Endianität ausgerichteter 16-Bit-Wörter aus. Eine Funktion, die häufig für UTF-16 / UCS-2-Codierungen erforderlich ist. EDIT END.
Wenn Sie die Endianess eines Speicherblocks ändern möchten, können Sie meinen blitzschnellen Ansatz verwenden. Ihr Speicherarray sollte eine Größe haben, die ein Vielfaches von 8 ist.
Diese Art von Funktion ist nützlich, um die Endianess von Unicode UCS-2 / UTF-16-Dateien zu ändern.
quelle
t know if it
so schnell wie die Vorschläge, aber es funktioniert: github.com/heatblazer/helpers/blob/master/utils.hCHAR_BIT
statt8
ist neugierig wie0xFF00FF00FF00FF00ULL
abhängig vonCHAR_BIT == 8
. Beachten Sie, dassLL
in der Konstante nicht benötigt.CHAR_BIT
, um die Belichtung dieses Makros zu erhöhen. Das LL ist mehr eine Anmerkung als alles andere. Es ist auch eine Angewohnheit, die ich vor langer Zeit mit Buggy-Compilern (vor dem Standard) hatte, die nicht das Richtige tun würden.Dieses Code-Snippet kann eine kleine 32-Bit-Endian-Nummer in eine Big-Endian-Nummer konvertieren.
quelle
((i>>24)&0xff) | ((i>>8)&0xff00) | ((i&0xff00)<<8) | (i<<24);
Auf einigen Plattformen ist die Verwendung möglicherweise schneller (z. B. Recycling der UND-Maskenkonstanten). Die meisten Compiler würden dies zwar tun, aber einige einfache Compiler können es nicht für Sie optimieren.Wenn Sie auf einem x86- oder x86_64-Prozessor arbeiten, ist der Big Endian nativ. so
für 16 Bit Werte
für 32-Bit-Werte
Dies ist nicht die effizienteste Lösung, es sei denn, der Compiler erkennt, dass dies eine Manipulation auf Byte-Ebene ist, und generiert Byte-Austauschcode. Es hängt jedoch nicht von irgendwelchen Speicherlayout-Tricks ab und kann ziemlich einfach in ein Makro umgewandelt werden.
quelle