konvertiere Big Endian in Little Endian in C [ohne Verwendung der bereitgestellten Funktion] [geschlossen]

91

Ich muss eine Funktion schreiben, um Big Endian in Little Endian in C zu konvertieren. Ich kann keine Bibliotheksfunktion verwenden.

Alex Xander
quelle
5
ein 16 Bit Wert? 32 Bit Wert? schweben? eine Anordnung?
John Knoeller
19
Zeit, vielleicht eine Antwort zu wählen?
Aniket Inge
7
Abstimmung zur Wiedereröffnung. Gleich wie stackoverflow.com/questions/105252/… für C ++. Wir könnten nur bearbeiten, um das klarer zu machen.
Ciro Santilli 8 冠状 病 六四 事件 8

Antworten:

167

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

Sam Post
quelle
5
+1 für die Erwähnung plattform- / hardwarespezifischer Methoden. Programme werden immer auf einer bestimmten Hardware ausgeführt, und die Hardwarefunktionen sind immer am schnellsten.
Eonil
21
Wenn die 16-Bit-Konvertierung wie folgt erfolgt ((num & 0xff) >> 8) | (num << 8), generiert gcc 4.8.3 einen einzelnen rolBefehl. Wenn die 32-Bit-Konvertierung als geschrieben wird ((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24), generiert derselbe Compiler einen einzelnen bswapBefehl.
user666412
Ich weiß nicht, wie effizient dies ist, aber ich habe die Bytereihenfolge gegen Bitfelder wie dieses ausgetauscht: 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.
Ilian Zapryanov
Ich denke, der Ausdruck muss sein: (num >> 8) | (num << 8), um die Bytereihenfolge umzukehren, und NICHT: ((num & 0xff) >> 8) | (num << 8), Das falsche Beispiel erhält im Low-Byte Null.
Jscom
@IlianZapryanov Vielleicht +1 aus Gründen der Klarheit, aber die Verwendung von Bitfeldern in C ist wahrscheinlich der am wenigsten effiziente Weg, dies zu tun.
Sherlellbc
104

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)
Amir Mgh
quelle
3
Vielen Dank für Ihre Antwort, aber ich kann keine Bibliotheksfunktion verwenden
Mark Ransom
4
Sollte lesen #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.
Eli Rosencruft
30
Tatsächlich sind die Funktionen __bswap_32 / __ bswap_16 Makros und keine Bibliotheksfunktionen, ein weiterer Grund für eine Abstimmung.
Eli Rosencruft
7
Nach meinem Verständnis ist nicht garantiert, dass dieser Header für alle Betriebssysteme auf allen Architekturen vorhanden ist. Ich habe noch keinen tragbaren Weg gefunden, um mit Endian-Problemen umzugehen.
Edward Falk
2
existiert nicht unter Windows - zumindest nicht beim Cross-Kompilieren von Linux mit Mingw 32 oder 64 Bit
bph
62
#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);
}
chmike
quelle
Was ist für die int32_tund int64_t-Varianten die Begründung für die Maskierung von ... & 0xFFFFund ... & 0xFFFFFFFFULL? Ist hier etwas mit Zeichenerweiterung los, das ich nicht sehe? Warum swap_int64kehrt man zurück uint64_t? Sollte das nicht sein int64_t?
Bgoodr
1
Der swap_int64, der einen uint64 zurückgibt, ist in der Tat ein Fehler. Die Maskierung mit vorzeichenbehafteten int-Werten dient in der Tat dazu, das Vorzeichen zu entfernen. Durch Verschieben nach rechts wird das Vorzeichenbit links eingefügt. Wir könnten dies vermeiden, indem wir einfach die vorzeichenlose int-Auslagerungsoperation aufrufen.
Chmike
Vielen Dank. Möglicherweise möchten Sie den Typ des Rückgabewerts swap_int64in Ihrer Antwort ändern . +1 für die hilfreiche Antwort, übrigens!
Bgoodr
Ist der bitweise und der Wert endian abhängig?
MarcusJ
1
Die LLsind unnötig, (u)swap_uint64()ähnlich wie ein Lnicht benötigt wird (u)swap_uint32(). Das Uwird nicht benötigt, uswap_uint64()ähnlich wie das Unicht benötigt wird inuswap_uint32()
chux - Reinstate Monica
13

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.

Michael J.
quelle
1
Sie können xorSwap für eine bessere Leistung verwenden. Bevorzugen Sie diese generische Version vor allem die
Ich habe es getestet, es stellt sich heraus, dass dies schneller ist als xorSwap ... auf x86. stackoverflow.com/questions/3128095/…
1
@nus - Einer der Vorteile von sehr einfachem Code ist, dass der Compiler-Optimierer ihn manchmal sehr schnell machen kann.
Michael J
@MichaelJ OTOH, die 32-Bit-Version oben in der Antwort von chmike wird bswapvon 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.
Alnitak
@Alnitak - Wie gesagt, ich habe mich nicht bemüht, meinen Code zu optimieren. Als Benutzer nus feststellte, dass der Code (in einem Fall) sehr schnell lief, erwähnte ich nur die allgemeine Idee, dass einfacher Code von einem Compiler häufig stark optimiert werden kann. Mein Code funktioniert für eine Vielzahl von Fällen und ist ziemlich leicht zu verstehen und somit leicht zu debuggen. Das hat meine Ziele erreicht.
Michael J
8

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))
kol
quelle
Diese Makros sind in Ordnung, aber ((x) >> 24) schlägt fehl, wenn eine vorzeichenbehaftete Ganzzahl zwischen 0x80000000 und 0xffffffff liegt. Es ist eine gute Idee, bitweise UND hier zu verwenden. Hinweis: ((x) << 24) ist absolut sicher. (x) >> 8) schlägt auch fehl, wenn hohe 16 Bit ungleich Null sind (oder ein vorzeichenbehafteter 16-Bit-Wert bereitgestellt wird).
2
@ PacMan-- Diese Makros sind nur zum Austauschen von Ganzzahlen ohne Vorzeichen vorgesehen . Deshalb gibt es die UINTin ihrem Namen.
kol
Ja, stimmt, entschuldige den Lärm. Wäre es nicht am besten, einen Typecast einzubetten?
5

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 bswapAnweisung, 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; 
    }
}
Sam Harwell
quelle
21
Für eine C-Frage schlagen Sie etwas vor, das spezifisch für Visual C ++ ist?
Alok Singhal
3
@Alok: Visual C ++ ist ein Produkt von Microsoft. Es funktioniert gut zum Kompilieren von C-Code. :)
Sam Harwell
20
Warum ist es für Sie verblüffend, dass viele Menschen nicht über Microsoft-spezifische Implementierungen von Byteswapping informiert sind?
Dreamlax
36
Cool, das sind gute Informationen für alle, die ein Closed-Source-Produkt entwickeln, das nicht tragbar oder standardkonform sein muss.
Sam Post
6
@Alok, OP hat den Compiler | OS nicht erwähnt. Eine Person darf entsprechend ihrer Erfahrung mit einem bestimmten Satz von Werkzeugen Antworten geben.
Aniket Inge
5

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;
}
Dreamlax
quelle
7
HAHAHAHAHA. Hahaha. Ha. Ha? (Was für ein Witz?)
3
Haben Sie dies aus einem Windows-Quellrepository abgerufen? :)
hochl
Nodejs benutzt diese Technik! github.com/nodejs/node/blob/…
Justin Moser
Neugierig zu verwenden int i, size_t sizeofIntund nicht der gleiche Typ für beide.
chux
5

Hier ist eine Möglichkeit, den SSSE3-Befehl pshufb unter Verwendung seiner Intel-Eigenschaft zu verwenden, vorausgesetzt, Sie haben ein Vielfaches von 4 ints:

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;
}
jcomeau_ictx
quelle
3

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];
Paul
quelle
2
Ich denke du meinst charnicht byte.
Dreamlax
Mit dieser Strategie ist die Lösung mit den meisten Stimmen im Vergleich zu Ihrer gleichwertig und die effizienteste und tragbarste. Die von mir vorgeschlagene Lösung (zweithäufigste Stimmen) erfordert jedoch weniger Operationen und sollte effizienter sein.
chmike
1

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

}
Ticketman
quelle
2
Der Code sourcebasiert auf einer sehr vernünftigen Annahme: Er wird nach Bedarf ausgerichtet. Wenn diese Annahme jedoch nicht zutrifft, lautet der Code UB.
chux
1

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.

Patrick Schlüter
quelle
CHAR_BIT #define fehlt, um den Code zu vervollständigen.
Tõnu Samuel
Ok, ich habe die fehlenden Includes hinzugefügt.
Patrick Schlüter
Hier ist ein Link zu einem Tausch in C ++, ich bin nicht t know if itso schnell wie die Vorschläge, aber es funktioniert: github.com/heatblazer/helpers/blob/master/utils.h
Ilian Zapryanov
CHAR_BITstatt 8ist neugierig wie 0xFF00FF00FF00FF00ULLabhängig von CHAR_BIT == 8. Beachten Sie, dass LLin der Konstante nicht benötigt.
chux
Du hast recht, Chux. Schrieb nur mit 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.
Patrick Schlüter
1

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);    
}
Kaushal Billore
quelle
Danke @YuHao Ich bin neu hier und weiß nicht, wie ich den Text formatieren soll.
Kaushal Billore
2
((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.
-7

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.

John Knoeller
quelle
25
Auf x86- und x86_64-Architekturen ist das Little-Endian-Schema das native.
MK aka Grisu