it-swarm.com.de

Big Endian in Little Endian in C konvertieren [ohne die mitgelieferte Funktion zu verwenden]

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

87
Alex Xander

Angenommen, Sie benötigen einen einfachen Bytetausch, versuchen Sie etwas wie

Vorzeichenlose 16-Bit-Konvertierung:

swapped = (num>>8) | (num<<8);

Vorzeichenlose 32-Bit-Konvertierung:

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

Dadurch werden die Bytereihenfolgen von Position 1234 auf 4321 vertauscht. Wenn Ihre Eingabe 0xdeadbeef, ein 32-Bit-Endian-Swap hat möglicherweise die Ausgabe von 0xefbeadde.

Der obige Code sollte mit Makros oder zumindest Konstanten anstelle von magischen Zahlen aufgeräumt werden, aber hoffentlich hilft es, wie es ist

BEARBEITEN: Wie eine andere Antwort hervorhob, gibt es plattform-, betriebssystem- und befehlssatzspezifische Alternativen, die VIEL schneller als die oben genannten sein können. Im Linux-Kernel gibt es Makros (zum Beispiel cpu_to_be32), die Endianness ziemlich gut handhaben. Diese Alternativen sind jedoch spezifisch für ihre Umgebung. In der Praxis wird Endianness am besten mit einer Mischung aus verfügbaren Ansätzen bewältigt

152
Sam Post

Durch Einbeziehung:

#include <byteswap.h>

sie können eine optimierte Version der maschinenabhängigen Byte-Swap-Funktionen erhalten. Dann können Sie leicht die folgenden Funktionen verwenden:

__bswap_32 (uint32_t input)

oder

__bswap_16 (uint16_t input)
92
Amir Mgh
#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);
}

pdate: 64bit Byte Swap 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);
}
55
chmike

Hier ist eine ziemlich generische 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 optimiert für Geschwindigkeit oder Raum. Es soll übersichtlich (leicht zu debuggen) und portabel sein.

pdate 2018-04-04 Es wurde assert () hinzugefügt, um den ungültigen Fall von n == 0 abzufangen, der vom Kommentator @chux entdeckt wurde.

12
Michael J

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))
8
kol

Bearbeiten: Dies sind Bibliotheksfunktionen. Ihnen zu folgen ist der manuelle Weg, dies zu tun.

Ich bin absolut verblüfft über die Anzahl der Menschen, die nichts von __ byteswap_ushort, __byteswap_ulong und __byteswap_uint64 wissen. Sicher, sie sind Visual C++ -spezifisch, aber sie kompilieren auf x86/IA-64-Architekturen bis auf einen köstlichen Code. :)

Hier ist eine explizite Verwendung der Anweisung bswap, von dieser Seite gezogen . Beachten Sie, dass die obige intrinsische Form immer schneller sein wird , ich habe sie nur hinzugefügt, um eine Antwort ohne a zu geben Bibliotheksroutine.

uint32 cq_ntohl(uint32 a) {
    __asm{
        mov eax, a;
        bswap eax; 
    }
}
6
Sam Harwell

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

hier ist eine Möglichkeit, den SSSE3-Befehl pshufb unter Verwendung seines Intel-Eigenzustands 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;
}
4
jcomeau_ictx

Wird das schneller gehen?

 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];
3
Paul

Dieses Code-Snippet kann 32-Bit-Little-Endian-Zahlen in Big-Endian-Zahlen 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);    
}
1
Kaushal Billore

BEARBEITEN: Diese Funktion tauscht nur die Endianzahl der ausgerichteten 16-Bit-Wörter. Eine Funktion, die häufig für UTF-16/UCS-2-Codierungen erforderlich ist. BEARBEITEN ENDE.

Wenn Sie die Endianess eines Speicherblocks ändern möchten, können Sie meinen blitzschnellen Ansatz verwenden. Ihr Speicherarray sollte ein Vielfaches von 8 haben.

#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 Endianität von Unicode-UCS-2/UTF-16-Dateien zu ändern.

1

Hier ist eine Funktion, die ich verwendet habe - getestet und für jeden grundlegenden Datentyp geeignet:

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

}
1
ticketman