Ich muss eine Funktion schreiben, um Big Endian in Little Endian in C zu konvertieren. Ich kann keine Bibliotheksfunktion verwenden.
Ich muss eine Funktion schreiben, um Big Endian in Little Endian in C zu konvertieren. Ich kann keine Bibliotheksfunktion verwenden.
Antworten:
Angenommen, Sie benötigen einen einfachen Byte-Austausch, versuchen Sie etwas wie
16-Bit-Konvertierung ohne Vorzeichen:
swapped = (num>>8) | (num<<8);
32-Bit-Konvertierung ohne Vorzeichen:
swapped = ((num>>24)&0xff) | // move byte 3 to byte 0
((num<<8)&0xff0000) | // move byte 1 to byte 2
((num>>8)&0xff00) | // move byte 2 to byte 1
((num<<24)&0xff000000); // byte 0 to byte 3
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 haben 0xefbeadde
.
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
((num & 0xff) >> 8) | (num << 8)
, generiert gcc 4.8.3 einen einzelnen rol
Befehl. Wenn die 32-Bit-Konvertierung als geschrieben wird ((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)
, generiert derselbe Compiler einen einzelnen bswap
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 Option union { int i; byte_t[sizeof(int)]; }
, Byte für Byte in der Ganzzahl umzukehren.
Durch Einbeziehung:
#include <byteswap.h>
Sie können eine optimierte Version der maschinenabhängigen Byte-Swapping-Funktionen erhalten. Dann können Sie einfach die folgenden Funktionen verwenden:
__bswap_32 (uint32_t input)
oder
__bswap_16 (uint16_t input)
#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.
#include <stdint.h>
//! Byte swap unsigned short
uint16_t swap_uint16( uint16_t val )
{
return (val << 8) | (val >> 8 );
}
//! Byte swap short
int16_t swap_int16( int16_t val )
{
return (val << 8) | ((val >> 8) & 0xFF);
}
//! Byte swap unsigned int
uint32_t swap_uint32( uint32_t val )
{
val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF );
return (val << 16) | (val >> 16);
}
//! Byte swap int
int32_t swap_int32( int32_t val )
{
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF );
return (val << 16) | ((val >> 16) & 0xFFFF);
}
Update : 64-Bit-Byte-Austausch hinzugefügt
int64_t swap_int64( int64_t val )
{
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL);
}
uint64_t swap_uint64( uint64_t val )
{
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | (val >> 32);
}
int32_t
und int64_t
-Varianten die Begründung für die Maskierung von ... & 0xFFFF
und ... & 0xFFFFFFFFULL
? Ist hier etwas mit Zeichenerweiterung los, das ich nicht sehe? Warum swap_int64
kehrt man zurück uint64_t
? Sollte das nicht sein int64_t
?
swap_int64
in Ihrer Antwort ändern . +1 für die hilfreiche Antwort, übrigens!
LL
sind unnötig, (u)swap_uint64()
ähnlich wie ein L
nicht benötigt wird (u)swap_uint32()
. Das U
wird nicht benötigt, uswap_uint64()
ähnlich wie das U
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,
void SwapBytes(void *pv, size_t n)
{
assert(n > 0);
char *p = pv;
size_t lo, hi;
for(lo=0, hi=n-1; hi>lo; lo++, hi--)
{
char tmp=p[lo];
p[lo] = p[hi];
p[hi] = tmp;
}
}
#define SWAP(x) SwapBytes(&x, sizeof(x));
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.
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):
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
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.
uint32 cq_ntohl(uint32 a) {
__asm{
mov eax, a;
bswap eax;
}
}
Als Witz:
#include <stdio.h>
int main (int argc, char *argv[])
{
size_t sizeofInt = sizeof (int);
int i;
union
{
int x;
char c[sizeof (int)];
} original, swapped;
original.x = 0x12345678;
for (i = 0; i < sizeofInt; i++)
swapped.c[sizeofInt - i - 1] = original.c[i];
fprintf (stderr, "%x\n", swapped.x);
return 0;
}
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:
unsigned int *bswap(unsigned int *destination, unsigned int *source, int length) {
int i;
__m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
for (i = 0; i < length; i += 4) {
_mm_storeu_si128((__m128i *)&destination[i],
_mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&source[i]), mask));
}
return destination;
}
Wird das schneller funktionieren / sein?
uint32_t swapped, result;
((byte*)&swapped)[0] = ((byte*)&result)[3];
((byte*)&swapped)[1] = ((byte*)&result)[2];
((byte*)&swapped)[2] = ((byte*)&result)[1];
((byte*)&swapped)[3] = ((byte*)&result)[0];
char
nicht byte
.
Hier ist eine Funktion, die ich verwendet habe - getestet und funktioniert mit jedem grundlegenden Datentyp:
// SwapBytes.h
//
// Function to perform in-place endian conversion of basic types
//
// Usage:
//
// double d;
// SwapBytes(&d, sizeof(d));
//
inline void SwapBytes(void *source, int size)
{
typedef unsigned char TwoBytes[2];
typedef unsigned char FourBytes[4];
typedef unsigned char EightBytes[8];
unsigned char temp;
if(size == 2)
{
TwoBytes *src = (TwoBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[1];
(*src)[1] = temp;
return;
}
if(size == 4)
{
FourBytes *src = (FourBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[3];
(*src)[3] = temp;
temp = (*src)[1];
(*src)[1] = (*src)[2];
(*src)[2] = temp;
return;
}
if(size == 8)
{
EightBytes *src = (EightBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[7];
(*src)[7] = temp;
temp = (*src)[1];
(*src)[1] = (*src)[6];
(*src)[6] = temp;
temp = (*src)[2];
(*src)[2] = (*src)[5];
(*src)[5] = temp;
temp = (*src)[3];
(*src)[3] = (*src)[4];
(*src)[4] = temp;
return;
}
}
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.
#include <stddef.h>
#include <limits.h>
#include <stdint.h>
void ChangeMemEndianness(uint64_t *mem, size_t size)
{
uint64_t m1 = 0xFF00FF00FF00FF00ULL, m2 = m1 >> CHAR_BIT;
size = (size + (sizeof (uint64_t) - 1)) / sizeof (uint64_t);
for(; size; size--, mem++)
*mem = ((*mem & m1) >> CHAR_BIT) | ((*mem & m2) << CHAR_BIT);
}
Diese Art von Funktion ist nützlich, um die Endianess von Unicode UCS-2 / UTF-16-Dateien zu ändern.
t know if it
so schnell wie die Vorschläge, aber es funktioniert: github.com/heatblazer/helpers/blob/master/utils.h
CHAR_BIT
statt 8
ist neugierig wie 0xFF00FF00FF00FF00ULL
abhängig von CHAR_BIT == 8
. Beachten Sie, dass LL
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.
#include <stdio.h>
main(){
unsigned int i = 0xfafbfcfd;
unsigned int j;
j= ((i&0xff000000)>>24)| ((i&0xff0000)>>8) | ((i&0xff00)<<8) | ((i&0xff)<<24);
printf("unsigned int j = %x\n ", j);
}
Wenn Sie auf einem x86- oder x86_64-Prozessor arbeiten, ist der Big Endian nativ. so
für 16 Bit Werte
unsigned short wBigE = value;
unsigned short wLittleE = ((wBigE & 0xFF) << 8) | (wBigE >> 8);
für 32-Bit-Werte
unsigned int iBigE = value;
unsigned int iLittleE = ((iBigE & 0xFF) << 24)
| ((iBigE & 0xFF00) << 8)
| ((iBigE >> 8) & 0xFF00)
| (iBigE >> 24);
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.