Wie kann ich zwischen Big-Endian und Little-Endian-Werte in C ++ konvertieren?

StackOverflow https://stackoverflow.com/questions/105252

  •  01-07-2019
  •  | 
  •  

Frage

Wie konvertiere ich zwischen Big-Endian und Little-Endian-Werte in C ++?

EDIT: Aus Gründen der Klarheit muß ich von einer CPU-Architektur zum anderen Binärdaten (double-precision Gleitkommawerte und 32-Bit- und 64-Bit-Integer) übersetzen. Dies beinhaltet keine Vernetzung, so ntoh () und ähnliche Funktionen werden hier nicht funktionieren.

EDIT # 2: Die Antwort, die ich gilt unmittelbar für Compiler akzeptiert Ich bin Targeting (weshalb ich es gewählt haben). Allerdings gibt es andere sehr gut, mehr tragbar Antworten hier.

War es hilfreich?

Lösung

Wenn Sie mit Visual C ++ Sie wie folgt vor: Sie sind intrin.h und rufen Sie folgende Funktionen:

Für 16-Bit-Zahlen:

unsigned short _byteswap_ushort(unsigned short value);

Für 32-Bit-Zahlen:

unsigned long _byteswap_ulong(unsigned long value);

Für 64-Bit-Zahlen:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8-Bit-Zahlen (Zeichen) müssen nicht konvertiert werden.

Auch nur diese werden definiert für nicht signierte Werte, die sie auch für signierte ganze Zahlen arbeiten.

Für den Schwimmer und verdoppelt es ist schwieriger als mit einfachen Zahlen wie diese dürfen oder nicht in der Host-Maschinen Byte-Reihenfolge sein kann. Sie können Little-Endian schwimmt auf Big-Endian-Maschinen bekommen und umgekehrt.

Andere Compiler haben auch ähnlich intrinsics.

GCC zum Beispiel können Sie direkt aufrufen:

int32_t __builtin_bswap32 (int32_t x)
int64_t __builtin_bswap64 (int64_t x)

(keine Notwendigkeit, etwas umfassen). Afaik bits.h die gleiche Funktion in einer nicht gcc-zentrierte Art und Weise erklärt, wie gut.

16-Bit-Swap es ist nur ein bisschen drehen.

, um die Spezifika-Aufruf stattdessen Ihre eigene Walz gibt Ihnen die beste Leistung und Codedichte btw ..

Andere Tipps

Einfach gesagt:

#include <climits>

template <typename T>
T swap_endian(T u)
{
    static_assert (CHAR_BIT == 8, "CHAR_BIT != 8");

    union
    {
        T u;
        unsigned char u8[sizeof(T)];
    } source, dest;

    source.u = u;

    for (size_t k = 0; k < sizeof(T); k++)
        dest.u8[k] = source.u8[sizeof(T) - k - 1];

    return dest.u;
}

Nutzung:. swap_endian<uint32_t>(42)

Die Byte-Reihenfolge Fallacy von Rob Pyke:

  

Lassen Sie sich sagen, dass Ihr Datenstrom hat eine Little-Endian-codierte 32-Bit-Integer. Hier ist, wie es zu extrahieren (unsigned Bytes vorausgesetzt):

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
  

Wenn es Big-Endian, hier ist, wie es zu extrahieren:

i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);

TL; DR: keine Sorge über Ihre Plattform nativen Ordnung, alles, was die Byte-Reihenfolge des Stroms zählt, ist Ihr aus lesen, und Sie hoffen, dass es gut definiert

Hinweis: es wurde im Kommentar bemerkt, die explizite Typumwandlung abwesend war es wichtig, dass data eine Reihe von unsigned char oder uint8_t sein. Verwendung signed char oder char (falls signiert) führt in data[x] auf eine ganze Zahl und data[x] << 24 potentiell eine 1 in das Vorzeichenbit, die UB verschiebt gefördert.

Wenn Sie dies zum Zweck des Netzwerk / Host-Kompatibilität tun sollten Sie verwenden:

ntohl() //Network to Host byte order (Long)
htonl() //Host to Network byte order (Long)

ntohs() //Network to Host byte order (Short)
htons() //Host to Network byte order (Short)

Wenn Sie dies aus einem anderen Grund eine der byte_swap Lösungen tun hier vorgestellt würde gut funktionieren.

Ich habe ein paar Vorschläge aus diesem Post und setze sie zusammen diese bilden:

#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/detail/endian.hpp>
#include <stdexcept>

enum endianness
{
    little_endian,
    big_endian,
    network_endian = big_endian,

    #if defined(BOOST_LITTLE_ENDIAN)
        host_endian = little_endian
    #elif defined(BOOST_BIG_ENDIAN)
        host_endian = big_endian
    #else
        #error "unable to determine system endianness"
    #endif
};

namespace detail {

template<typename T, size_t sz>
struct swap_bytes
{
    inline T operator()(T val)
    {
        throw std::out_of_range("data size");
    }
};

template<typename T>
struct swap_bytes<T, 1>
{
    inline T operator()(T val)
    {
        return val;
    }
};

template<typename T>
struct swap_bytes<T, 2>
{
    inline T operator()(T val)
    {
        return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
    }
};

template<typename T>
struct swap_bytes<T, 4>
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff000000) >> 24) |
                (((val) & 0x00ff0000) >>  8) |
                (((val) & 0x0000ff00) <<  8) |
                (((val) & 0x000000ff) << 24));
    }
};

template<>
struct swap_bytes<float, 4>
{
    inline float operator()(float val)
    {
        uint32_t mem =swap_bytes<uint32_t, sizeof(uint32_t)>()(*(uint32_t*)&val);
        return *(float*)&mem;
    }
};

template<typename T>
struct swap_bytes<T, 8>
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff00000000000000ull) >> 56) |
                (((val) & 0x00ff000000000000ull) >> 40) |
                (((val) & 0x0000ff0000000000ull) >> 24) |
                (((val) & 0x000000ff00000000ull) >> 8 ) |
                (((val) & 0x00000000ff000000ull) << 8 ) |
                (((val) & 0x0000000000ff0000ull) << 24) |
                (((val) & 0x000000000000ff00ull) << 40) |
                (((val) & 0x00000000000000ffull) << 56));
    }
};

template<>
struct swap_bytes<double, 8>
{
    inline double operator()(double val)
    {
        uint64_t mem =swap_bytes<uint64_t, sizeof(uint64_t)>()(*(uint64_t*)&val);
        return *(double*)&mem;
    }
};

template<endianness from, endianness to, class T>
struct do_byte_swap
{
    inline T operator()(T value)
    {
        return swap_bytes<T, sizeof(T)>()(value);
    }
};
// specialisations when attempting to swap to the same endianess
template<class T> struct do_byte_swap<little_endian, little_endian, T> { inline T operator()(T value) { return value; } };
template<class T> struct do_byte_swap<big_endian,    big_endian,    T> { inline T operator()(T value) { return value; } };

} // namespace detail

template<endianness from, endianness to, class T>
inline T byte_swap(T value)
{
    // ensure the data is only 1, 2, 4 or 8 bytes
    BOOST_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);
    // ensure we're only swapping arithmetic types
    BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);

    return detail::do_byte_swap<from, to, T>()(value);
}

Es gibt eine Montageanleitung genannt BSWAP, die den Swap für Sie tun, extrem schnell . Sie können darüber hier .

Visual Studio, oder genauer gesagt, die Visual C ++ Laufzeitbibliothek, hat Plattform-Spezifika für diese, die so genannten _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Ähnliche sollte für andere Plattformen existiert, aber ich bin mir nicht bewusst, was würden sie aufgerufen werden.

Das Verfahren für den Gang von Big-Endian zu Little-Endian ist die gleiche wie von Little-Endian zu Big-Endian gehen.

Hier ist ein Beispielcode:

void swapByteOrder(unsigned short& us)
{
    us = (us >> 8) |
         (us << 8);
}

void swapByteOrder(unsigned int& ui)
{
    ui = (ui >> 24) |
         ((ui<<8) & 0x00FF0000) |
         ((ui>>8) & 0x0000FF00) |
         (ui << 24);
}

void swapByteOrder(unsigned long long& ull)
{
    ull = (ull >> 56) |
          ((ull<<40) & 0x00FF000000000000) |
          ((ull<<24) & 0x0000FF0000000000) |
          ((ull<<8) & 0x000000FF00000000) |
          ((ull>>8) & 0x00000000FF000000) |
          ((ull>>24) & 0x0000000000FF0000) |
          ((ull>>40) & 0x000000000000FF00) |
          (ull << 56);
}

Wir haben dies mit Vorlagen gemacht. Man könnte so etwas wie folgt aus:

// Specialization for 2-byte types.
template<>
inline void endian_byte_swapper< 2 >(char* dest, char const* src)
{
    // Use bit manipulations instead of accessing individual bytes from memory, much faster.
    ushort* p_dest = reinterpret_cast< ushort* >(dest);
    ushort const* const p_src = reinterpret_cast< ushort const* >(src);
    *p_dest = (*p_src >> 8) | (*p_src << 8);
}

// Specialization for 4-byte types.
template<>
inline void endian_byte_swapper< 4 >(char* dest, char const* src)
{
    // Use bit manipulations instead of accessing individual bytes from memory, much faster.
    uint* p_dest = reinterpret_cast< uint* >(dest);
    uint const* const p_src = reinterpret_cast< uint const* >(src);
    *p_dest = (*p_src >> 24) | ((*p_src & 0x00ff0000) >> 8) | ((*p_src & 0x0000ff00) << 8) | (*p_src << 24);
}

Wenn Sie tun dies Blick auf den ntoh und Hton Funktionen zwischen verschiedenen Plattformen Daten zu übertragen.

Auf die gleiche Weise Sie in C tun:

short big = 0xdead;
short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8));

Sie können auch einen Vektor von unsigned chars, MEMCPY den Eingangswert in sie, umgekehrt die Bytes in einen anderen Vektor erklären und die Bytes MEMCPY, aber das wird um Größenordnungen länger als Bit-Fummeln, vor allem mit 64- Bit-Werte.

Bei den meisten POSIX-Systemen (durch es ist nicht in dem POSIX-Standard) gibt es die endian.h, die verwendet werden können, um zu bestimmen, was kodiert Ihr System verwendet. Von dort ist es so etwas wie folgt aus:

unsigned int change_endian(unsinged int x)
{
    unsigned char *ptr = (unsigned char *)&x;
    return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
}

Diese tauscht die Reihenfolge (von Big-Endian zu Little-Endian):

Wenn Sie die Nummer 0xDEADBEEF (auf Little-Endian-System als 0xEFBEADDE gespeichert), ptr [0] wird 0xEF, ptr [1] ist 0xBE, etc.

Aber wenn Sie wollen, es zu benutzen für die Vernetzung, dann htons, htonl und htonll (und die inverse ntohs, ntohl und ntohll) wird die Umwandlung von Host-Order hilfreich sein, um zu vernetzen.

Beachten Sie, dass, zumindest für Windows, htonl () ist viel langsamer als ihr inneres Gegenstück _byteswap_ulong (). Ersteres ist eine DLL-Bibliothek Aufruf in ws2_32.dll, letztere ist eine BSWAP Montageanleitung. Deshalb, wenn Sie einige plattformabhängige Code schreiben, bevorzugen die Spezifika für Geschwindigkeit mit:

#define htonl(x) _byteswap_ulong(x)

Dies kann besonders wichtig sein für PNG- Bildverarbeitung, in der alle Zahlen in Big Endian mit Erklärung gespeichert „Man kann htonl () verwenden ...“ {verlangsamen typische Windows-Programme, wenn Sie nicht bereit sind}.

Die meisten Plattformen haben ein System-Header-Datei, die eine effiziente byteswap Funktionen zur Verfügung stellt. Unter Linux ist es in <endian.h>. Sie können es schön in C ++ wickeln:

#include <iostream>

#include <endian.h>

template<size_t N> struct SizeT {};

#define BYTESWAPS(bits) \
template<class T> inline T htobe(T t, SizeT<bits / 8>) { return htobe ## bits(t); } \
template<class T> inline T htole(T t, SizeT<bits / 8>) { return htole ## bits(t); } \
template<class T> inline T betoh(T t, SizeT<bits / 8>) { return be ## bits ## toh(t); } \
template<class T> inline T letoh(T t, SizeT<bits / 8>) { return le ## bits ## toh(t); }

BYTESWAPS(16)
BYTESWAPS(32)
BYTESWAPS(64)

#undef BYTESWAPS

template<class T> inline T htobe(T t) { return htobe(t, SizeT<sizeof t>()); }
template<class T> inline T htole(T t) { return htole(t, SizeT<sizeof t>()); }
template<class T> inline T betoh(T t) { return betoh(t, SizeT<sizeof t>()); }
template<class T> inline T letoh(T t) { return letoh(t, SizeT<sizeof t>()); }

int main()
{
    std::cout << std::hex;
    std::cout << htobe(static_cast<unsigned short>(0xfeca)) << '\n';
    std::cout << htobe(0xafbeadde) << '\n';

    // Use ULL suffix to specify integer constant as unsigned long long 
    std::cout << htobe(0xfecaefbeafdeedfeULL) << '\n';
}

Ausgabe:

cafe
deadbeaf
feeddeafbeefcafe

Ich mag dieses, nur für Stil: -)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}

Im Ernst ... Ich verstehe nicht, warum alle Lösungen sind, dass kompliziert ! Wie wäre es am einfachsten, allgemeinsten Template-Funktion, die jede Art von jeder Größe unter keinen Umständen in jedem Betriebssystem austauscht ????

template <typename T>
void SwapEnd(T& var)
{
    char* varArray = reinterpret_cast<char*>(&var);
    for(long i = 0; i < static_cast<long>(sizeof(var)/2); i++)
        std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
}

Es ist die magische Kraft von C und C ++ zusammen! tauschen Sie einfach die ursprüngliche Variable Zeichen für Zeichen.

Beachten Sie, dass ich nicht genutzt haben werden, um den einfachen Zuweisungsoperator „=“ da einige Objekte durcheinander, wenn das Endian blättert und der Copykonstruktor (oder Zuweisungsoperator) wird nicht funktionieren. Daher ist es sicherer, sich char von char zu kopieren.

es zu nennen, benutzen Sie einfach

double x = 5;
SwapEnd(x);

und jetzt x unterscheidet sich in endianness.

Ich habe diesen Code, der mich von HOST_ENDIAN_ORDER umwandeln lassen (was auch immer es ist) zu LITTLE_ENDIAN_ORDER oder BIG_ENDIAN_ORDER. Ich benutze eine Schablone, also wenn ich versuche, von HOST_ENDIAN_ORDER zu LITTLE_ENDIAN_ORDER zu konvertieren und sie passieren das gleiche für die Maschine für Wich sein, die ich zu kompilieren, wird kein Code generiert werden.

Hier ist der Code mit einigen Kommentaren:

// We define some constant for little, big and host endianess. Here I use 
// BOOST_LITTLE_ENDIAN/BOOST_BIG_ENDIAN to check the host indianess. If you
// don't want to use boost you will have to modify this part a bit.
enum EEndian
{
  LITTLE_ENDIAN_ORDER,
  BIG_ENDIAN_ORDER,
#if defined(BOOST_LITTLE_ENDIAN)
  HOST_ENDIAN_ORDER = LITTLE_ENDIAN_ORDER
#elif defined(BOOST_BIG_ENDIAN)
  HOST_ENDIAN_ORDER = BIG_ENDIAN_ORDER
#else
#error "Impossible de determiner l'indianness du systeme cible."
#endif
};

// this function swap the bytes of values given it's size as a template
// parameter (could sizeof be used?).
template <class T, unsigned int size>
inline T SwapBytes(T value)
{
  union
  {
     T value;
     char bytes[size];
  } in, out;

  in.value = value;

  for (unsigned int i = 0; i < size / 2; ++i)
  {
     out.bytes[i] = in.bytes[size - 1 - i];
     out.bytes[size - 1 - i] = in.bytes[i];
  }

  return out.value;
}

// Here is the function you will use. Again there is two compile-time assertion
// that use the boost librarie. You could probably comment them out, but if you
// do be cautious not to use this function for anything else than integers
// types. This function need to be calles like this :
//
//     int x = someValue;
//     int i = EndianSwapBytes<HOST_ENDIAN_ORDER, BIG_ENDIAN_ORDER>(x);
//
template<EEndian from, EEndian to, class T>
inline T EndianSwapBytes(T value)
{
  // A : La donnée à swapper à une taille de 2, 4 ou 8 octets
  BOOST_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);

  // A : La donnée à swapper est d'un type arithmetic
  BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);

  // Si from et to sont du même type on ne swap pas.
  if (from == to)
     return value;

  return SwapBytes<T, sizeof(T)>(value);
}

Wenn ein Big-Endian-32-Bit-Integer ohne Vorzeichen sieht aus wie 0xAABBCCDD, die 2864434397 gleich ist, dann ist das gleiche 32-Bit-Integer ohne Vorzeichen wie 0xDDCCBBAA auf einem Little-Endian-Prozessor sieht, die 2864434397 auch gleich ist.

Wenn ein Big-Endian-16-Bit unsigned short sieht aus wie 0xAABB, die auf 43.707 gleich ist, dann ist das gleiche 16-Bit unsigned short sieht aus wie 0xBBAA auf einem Little-Endian-Prozessor, der auf 43707 auch gleich ist.

Hier sind ein paar handliche #define Funktionen Bytes von Little-Endian zu tauschen, um Big-Endian und umgekehrt ->

// can be used for short, unsigned short, word, unsigned word (2-byte types)
#define BYTESWAP16(n) (((n&0xFF00)>>8)|((n&0x00FF)<<8))

// can be used for int or unsigned int or float (4-byte types)
#define BYTESWAP32(n) ((BYTESWAP16((n&0xFFFF0000)>>16))|((BYTESWAP16(n&0x0000FFFF))<<16))

// can be used for unsigned long long or double (8-byte types)
#define BYTESWAP64(n) ((BYTESWAP32((n&0xFFFFFFFF00000000)>>32))|((BYTESWAP32(n&0x00000000FFFFFFFF))<<32))

Hier ist eine verallgemeinerte Version I mit aus der Spitze von meinem Kopf kam, für einen Wert im Ort austauschen. Die anderen Vorschläge wäre besser, wenn die Leistung ein Problem ist.

 template<typename T>
    void ByteSwap(T * p)
    {
        for (int i = 0;  i < sizeof(T)/2;  ++i)
            std::swap(((char *)p)[i], ((char *)p)[sizeof(T)-1-i]);
    }

Haftungsausschluss:. Ich habe nicht versucht zu kompilieren oder es noch testen

Wenn Sie das gemeinsame Muster, um die Reihenfolge der Bits in einem Wort zum Umkehren und keulen des Teils, die Bits in jedem Byte umkehrt, dann sind Sie mit etwas übrig, die nur das Bytes innerhalb eines Wortes umkehrt. Für 64-Bit:

x = ((x & 0x00000000ffffffff) << 32) ^ ((x >> 32) & 0x00000000ffffffff);
x = ((x & 0x0000ffff0000ffff) << 16) ^ ((x >> 16) & 0x0000ffff0000ffff);
x = ((x & 0x00ff00ff00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff00ff00ff);

Der Compiler sollte reinigen, die überflüssigen Bit-Maskierungsoperationen (ich sie links in das Muster zu markieren), aber wenn es nicht der ersten Zeile auf diese Weise umschreiben kann:

x = ( x                       << 32) ^  (x >> 32);

Das sollte normalerweise vereinfachen auf den meisten Architekturen zu einem einzigen Rotieroperation nach unten (ohne Berücksichtigung, dass die ganze Operation wahrscheinlich eine Anweisung ist).

Auf einem RISC-Prozessor die großen, komplizierten Konstanten können die Compiler Schwierigkeiten verursachen. Sie können trivialerweise jede der Konstanten von der vorherigen Berechnung, though. Wie so:

uint64_t k = 0x00000000ffffffff; /* compiler should know a trick for this */
x = ((x & k) << 32) ^ ((x >> 32) & k);
k ^= k << 16;
x = ((x & k) << 16) ^ ((x >> 16) & k);
k ^= k << 8;
x = ((x & k) <<  8) ^ ((x >>  8) & k);

Wenn Sie möchten, können Sie das als Schleife schreiben. Es wird nicht effizient sein, aber nur zum Spaß:

int i = sizeof(x) * CHAR_BIT / 2;
uintmax_t k = (1 << i) - 1;
while (i >= 8)
{
    x = ((x & k) << i) ^ ((x >> i) & k);
    i >>= 1;
    k ^= k << i;
}

Für Vollständigkeit, hier ist die vereinfachte 32-Bit-Version der ersten Form:

x = ( x               << 16) ^  (x >> 16);
x = ((x & 0x00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff);

dachte nur, dass ich meine eigene Lösung hier hinzugefügt, da ich es nicht überall gesehen. Es ist eine kleine und portable C ++ Templat Funktion und tragbar, dass nur Bit-Operationen verwendet wird.

template<typename T> inline static T swapByteOrder(const T& val) {
    int totalBytes = sizeof(val);
    T swapped = (T) 0;
    for (int i = 0; i < totalBytes; ++i) {
        swapped |= (val >> (8*(totalBytes-i-1)) & 0xFF) << (8*i);
    }
    return swapped;
}

Mit den unten angegebenen Codes, die Sie leicht zwischen BigEndian und LittleEndian tauschen

#define uint32_t unsigned 
#define uint16_t unsigned short

#define swap16(x) ((((uint16_t)(x) & 0x00ff)<<8)| \
(((uint16_t)(x) & 0xff00)>>8))

#define swap32(x) ((((uint32_t)(x) & 0x000000ff)<<24)| \
(((uint32_t)(x) & 0x0000ff00)<<8)| \
(((uint32_t)(x) & 0x00ff0000)>>8)| \
(((uint32_t)(x) & 0xff000000)>>24))

Ich bin wirklich überrascht niemanden htobeXX und betohXX Funktionen genannt. Sie sind in endian.h definiert und sind sehr ähnlich zu Netzwerkfunktionen htonXX.

Wow, ich kann nicht einige der Antworten glaube ich hier gelesen habe. Es gibt tatsächlich eine Anweisung in der Montage, die diese schneller macht als alles andere. bswap. Sie könnten einfach eine Funktion wie diese schreiben ...

__declspec(naked) uint32_t EndianSwap(uint32 value)
{
    __asm
    {
        mov eax, dword ptr[esp + 4]
        bswap eax
        ret
    }
}

Es ist VIEL schneller als die Spezifika, die vorgeschlagen wurden. Ich habe sie auseinander genommen und sah. Die obige Funktion hat keinen Prolog / Epilog so hat praktisch keinen Aufwand überhaupt.

unsigned long _byteswap_ulong(unsigned long value);

16 Bit zu tun, ist genauso einfach, mit der Ausnahme, dass Sie al verwenden xchg würde, ah. bswap funktioniert nur auf 32-Bit-Register.

64-Bit ist ein wenig komplizierter, aber nicht übermäßig. Viel besser als alle oben genannten Beispiele mit Loops und Vorlagen etc.

Es gibt einige Einsprüche hier ... Zum einen ist bswap nur auf 80x486 CPUs verfügbar und darüber. Ist jemand plant, auf sie auf einem 386 laufen?!? Wenn ja, können Sie immer noch ersetzen bswap mit ...

mov ebx, eax
shr ebx, 16
xchg bl, bh
xchg al, ah
shl eax, 16
or eax, ebx

Auch Inline-Assembler ist in x86-Code in Visual Studio zur Verfügung. Eine nackte Funktion kann nicht gefüttert werden, und auch nicht in x64 baut. Ich diese Instanz, Sie gehen zu müssen, um das Compiler-Spezifika verwenden.

Tragbare Technik für Optimierer freundliche unaligned Nicht-Inplace-Endian-Accessoren implementieren. Sie arbeiten an jedem Compiler, jede Grenze Ausrichtung und jedes Byte-Reihenfolge. Diese unaligned Routinen ergänzt, oder zur Debatte, abhängig von nativen Endian und Ausrichtung. Unvollständige Auflistung, aber Sie bekommen die Idee. BO * konstante Werte auf Basis nativer Byteanordnung.

uint32_t sw_get_uint32_1234(pu32)
uint32_1234 *pu32;
{
  union {
    uint32_1234 u32_1234;
    uint32_t u32;
  } bou32;
  bou32.u32_1234[0] = (*pu32)[BO32_0];
  bou32.u32_1234[1] = (*pu32)[BO32_1];
  bou32.u32_1234[2] = (*pu32)[BO32_2];
  bou32.u32_1234[3] = (*pu32)[BO32_3];
  return(bou32.u32);
}

void sw_set_uint32_1234(pu32, u32)
uint32_1234 *pu32;
uint32_t u32;
{
  union {
    uint32_1234 u32_1234;
    uint32_t u32;
  } bou32;
  bou32.u32 = u32;
  (*pu32)[BO32_0] = bou32.u32_1234[0];
  (*pu32)[BO32_1] = bou32.u32_1234[1];
  (*pu32)[BO32_2] = bou32.u32_1234[2];
  (*pu32)[BO32_3] = bou32.u32_1234[3];
}

#if HAS_SW_INT64
int64 sw_get_int64_12345678(pi64)
int64_12345678 *pi64;
{
  union {
    int64_12345678 i64_12345678;
    int64 i64;
  } boi64;
  boi64.i64_12345678[0] = (*pi64)[BO64_0];
  boi64.i64_12345678[1] = (*pi64)[BO64_1];
  boi64.i64_12345678[2] = (*pi64)[BO64_2];
  boi64.i64_12345678[3] = (*pi64)[BO64_3];
  boi64.i64_12345678[4] = (*pi64)[BO64_4];
  boi64.i64_12345678[5] = (*pi64)[BO64_5];
  boi64.i64_12345678[6] = (*pi64)[BO64_6];
  boi64.i64_12345678[7] = (*pi64)[BO64_7];
  return(boi64.i64);
}
#endif

int32_t sw_get_int32_3412(pi32)
int32_3412 *pi32;
{
  union {
    int32_3412 i32_3412;
    int32_t i32;
  } boi32;
  boi32.i32_3412[2] = (*pi32)[BO32_0];
  boi32.i32_3412[3] = (*pi32)[BO32_1];
  boi32.i32_3412[0] = (*pi32)[BO32_2];
  boi32.i32_3412[1] = (*pi32)[BO32_3];
  return(boi32.i32);
}

void sw_set_int32_3412(pi32, i32)
int32_3412 *pi32;
int32_t i32;
{
  union {
    int32_3412 i32_3412;
    int32_t i32;
  } boi32;
  boi32.i32 = i32;
  (*pi32)[BO32_0] = boi32.i32_3412[2];
  (*pi32)[BO32_1] = boi32.i32_3412[3];
  (*pi32)[BO32_2] = boi32.i32_3412[0];
  (*pi32)[BO32_3] = boi32.i32_3412[1];
}

uint32_t sw_get_uint32_3412(pu32)
uint32_3412 *pu32;
{
  union {
    uint32_3412 u32_3412;
    uint32_t u32;
  } bou32;
  bou32.u32_3412[2] = (*pu32)[BO32_0];
  bou32.u32_3412[3] = (*pu32)[BO32_1];
  bou32.u32_3412[0] = (*pu32)[BO32_2];
  bou32.u32_3412[1] = (*pu32)[BO32_3];
  return(bou32.u32);
}

void sw_set_uint32_3412(pu32, u32)
uint32_3412 *pu32;
uint32_t u32;
{
  union {
    uint32_3412 u32_3412;
    uint32_t u32;
  } bou32;
  bou32.u32 = u32;
  (*pu32)[BO32_0] = bou32.u32_3412[2];
  (*pu32)[BO32_1] = bou32.u32_3412[3];
  (*pu32)[BO32_2] = bou32.u32_3412[0];
  (*pu32)[BO32_3] = bou32.u32_3412[1];
}

float sw_get_float_1234(pf)
float_1234 *pf;
{
  union {
    float_1234 f_1234;
    float f;
  } bof;
  bof.f_1234[0] = (*pf)[BO32_0];
  bof.f_1234[1] = (*pf)[BO32_1];
  bof.f_1234[2] = (*pf)[BO32_2];
  bof.f_1234[3] = (*pf)[BO32_3];
  return(bof.f);
}

void sw_set_float_1234(pf, f)
float_1234 *pf;
float f;
{
  union {
    float_1234 f_1234;
    float f;
  } bof;
  bof.f = (float)f;
  (*pf)[BO32_0] = bof.f_1234[0];
  (*pf)[BO32_1] = bof.f_1234[1];
  (*pf)[BO32_2] = bof.f_1234[2];
  (*pf)[BO32_3] = bof.f_1234[3];
}

double sw_get_double_12345678(pd)
double_12345678 *pd;
{
  union {
    double_12345678 d_12345678;
    double d;
  } bod;
  bod.d_12345678[0] = (*pd)[BO64_0];
  bod.d_12345678[1] = (*pd)[BO64_1];
  bod.d_12345678[2] = (*pd)[BO64_2];
  bod.d_12345678[3] = (*pd)[BO64_3];
  bod.d_12345678[4] = (*pd)[BO64_4];
  bod.d_12345678[5] = (*pd)[BO64_5];
  bod.d_12345678[6] = (*pd)[BO64_6];
  bod.d_12345678[7] = (*pd)[BO64_7];
  return(bod.d);
}

void sw_set_double_12345678(pd, d)
double_12345678 *pd;
double d;
{
  union {
    double_12345678 d_12345678;
    double d;
  } bod;
  bod.d = d;
  (*pd)[BO64_0] = bod.d_12345678[0];
  (*pd)[BO64_1] = bod.d_12345678[1];
  (*pd)[BO64_2] = bod.d_12345678[2];
  (*pd)[BO64_3] = bod.d_12345678[3];
  (*pd)[BO64_4] = bod.d_12345678[4];
  (*pd)[BO64_5] = bod.d_12345678[5];
  (*pd)[BO64_6] = bod.d_12345678[6];
  (*pd)[BO64_7] = bod.d_12345678[7];
}

Diese typedefs haben den Vorteil, Compiler-Fehler zu heben, wenn nicht mit Accessoren verwendet, so vergessen Accessor Fehler zu mildern.

typedef char int8_1[1], uint8_1[1];

typedef char int16_12[2], uint16_12[2]; /* little endian */
typedef char int16_21[2], uint16_21[2]; /* big endian */

typedef char int24_321[3], uint24_321[3]; /* Alpha Micro, PDP-11 */

typedef char int32_1234[4], uint32_1234[4]; /* little endian */
typedef char int32_3412[4], uint32_3412[4]; /* Alpha Micro, PDP-11 */
typedef char int32_4321[4], uint32_4321[4]; /* big endian */

typedef char int64_12345678[8], uint64_12345678[8]; /* little endian */
typedef char int64_34128756[8], uint64_34128756[8]; /* Alpha Micro, PDP-11 */
typedef char int64_87654321[8], uint64_87654321[8]; /* big endian */

typedef char float_1234[4]; /* little endian */
typedef char float_3412[4]; /* Alpha Micro, PDP-11 */
typedef char float_4321[4]; /* big endian */

typedef char double_12345678[8]; /* little endian */
typedef char double_78563412[8]; /* Alpha Micro? */
typedef char double_87654321[8]; /* big endian */

Ich schrieb vor kurzem ein Makro dies in C zu tun, aber es ist genauso gültig in C ++:

#define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES<sizeof(__VA_ARGS__)>>1; ++REVERSE_BYTES)\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES];\
while(0)

Es akzeptiert jede Art und kehrt das Bytes in dem übergebenen Argumente. Beispiele für den Gebrauch:

int main(){
    unsigned long long x = 0xABCDEF0123456789;
    printf("Before: %llX\n",x);
    REVERSE_BYTES(x);
    printf("After : %llX\n",x);

    char c[7]="nametag";
    printf("Before: %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
    REVERSE_BYTES(c);
    printf("After : %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
}

Welche druckt:

Before: ABCDEF0123456789
After : 8967452301EFCDAB
Before: nametag
After : gateman

Das oben ist perfekt Kopieren / Einfügen-fähig, aber es gibt viel los hier, also werde ich brechen, wie es Stück für Stück arbeitet:

Die erste bemerkenswerte Sache ist, dass das gesamte Makro in einem do while(0) Block eingeschlossen ist. Dies ist ein gemeinsames Idiom normale Semikolon Verwendung nach dem erlauben, Makro.

Als nächstes ist die Verwendung einer Variablen mit dem Namen REVERSE_BYTES als die Zähler for Schleife. Der Name des Makros selbst wird als Variablenname verwendet, um sicherzustellen, dass es nicht mit anderen Symbolen kollidiert, die in ihrem Umfang sein können, wo immer das Makro verwendet wird. Da der Name innerhalb der Makro-Expansion verwendet wird, wird es nicht wieder, wenn sie als ein Variablenname verwendet erweitert werden.

Im for Schleife gibt es zwei Bytes verwiesen wird und XOR getauscht (so ein temporärer Variablenname ist nicht erforderlich):

((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES]
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES]

__VA_ARGS__ darstellt, was auch immer an das Makro gegeben wurde, und ist verwendet, um die Flexibilität zu erhöhen, was in (wenn auch nicht viel) übergeben werden kann. Die Adresse dieses Arguments wird dann genommen und zu goss einen unsigned char Zeiger den swapping seines Bytes über Array [] Subskribierung zu ermöglichen.

Der letzte eigentümliche Punkt ist der Mangel an {} Klammern. Sie sind nicht erforderlich, da alle Schritte in jedem Swap verbunden sind mit dem Komma-Operator , so dass sie eine Erklärung ab.

Schließlich ist es erwähnenswert, dass dies nicht der ideale Ansatz ist, wenn die Geschwindigkeit oberste Priorität ist. Wenn dies ein wichtiger Faktor ist, sind in anderen Antworten verwiesen einige der typspezifischen Makros oder plattformspezifischen Richtlinien wahrscheinlich eine bessere Option. Dieser Ansatz ist jedoch tragbar für alle Arten, alle wichtigen Plattformen und sowohl der C- und C ++ Sprachen.

Versuchen Boost::endian und nicht implementieren ES SICH!

Hier ist ein Link

Hier ist, wie ein Doppel in IEEE 754 64-Bit-Format, auch wenn Ihr Host-Computer verwendet ein anderes System gespeichert zu lesen.

/*
* read a double from a stream in ieee754 format regardless of host
*  encoding.
*  fp - the stream
*  bigendian - set to if big bytes first, clear for little bytes
*              first
*
*/
double freadieee754(FILE *fp, int bigendian)
{
    unsigned char buff[8];
    int i;
    double fnorm = 0.0;
    unsigned char temp;
    int sign;
    int exponent;
    double bitval;
    int maski, mask;
    int expbits = 11;
    int significandbits = 52;
    int shift;
    double answer;

    /* read the data */
    for (i = 0; i < 8; i++)
        buff[i] = fgetc(fp);
    /* just reverse if not big-endian*/
    if (!bigendian)
    {
        for (i = 0; i < 4; i++)
        {
            temp = buff[i];
            buff[i] = buff[8 - i - 1];
            buff[8 - i - 1] = temp;
        }
    }
    sign = buff[0] & 0x80 ? -1 : 1;
    /* exponet in raw format*/
    exponent = ((buff[0] & 0x7F) << 4) | ((buff[1] & 0xF0) >> 4);

    /* read inthe mantissa. Top bit is 0.5, the successive bits half*/
    bitval = 0.5;
    maski = 1;
    mask = 0x08;
    for (i = 0; i < significandbits; i++)
    {
        if (buff[maski] & mask)
            fnorm += bitval;

        bitval /= 2.0;
        mask >>= 1;
        if (mask == 0)
        {
            mask = 0x80;
            maski++;
        }
    }
    /* handle zero specially */
    if (exponent == 0 && fnorm == 0)
        return 0.0;

    shift = exponent - ((1 << (expbits - 1)) - 1); /* exponent = shift + bias */
    /* nans have exp 1024 and non-zero mantissa */
    if (shift == 1024 && fnorm != 0)
        return sqrt(-1.0);
    /*infinity*/
    if (shift == 1024 && fnorm == 0)
    {

#ifdef INFINITY
        return sign == 1 ? INFINITY : -INFINITY;
#endif
        return  (sign * 1.0) / 0.0;
    }
    if (shift > -1023)
    {
        answer = ldexp(fnorm + 1.0, shift);
        return answer * sign;
    }
    else
    {
        /* denormalised numbers */
        if (fnorm == 0.0)
            return 0.0;
        shift = -1022;
        while (fnorm < 1.0)
        {
            fnorm *= 2;
            shift--;
        }
        answer = ldexp(fnorm, shift);
        return answer * sign;
    }
}

Für den Rest der Suite von Funktionen, einschließlich der Schreib- und die Integer-Routinen finden Sie in meinem GitHub Projekt

https://github.com/MalcolmMcLean/ieee754

Byte mit ihr tauschen olde 3-Schritt-xor Trick um einen Drehpunkt in einer Template-Funktion gibt ein flexible, schnelles O (LN2) Lösung, die nicht über eine Bibliothek erfordert, hier der Stil lehnt auch 1 Byte-Typen:

template<typename T>void swap(T &t){
    for(uint8_t pivot = 0; pivot < sizeof(t)/2; pivot ++){
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
        *((uint8_t *)&t+sizeof(t)-1- pivot) ^= *((uint8_t *)&t + pivot);
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
    }
}

Scheint, wie der sichere Weg htons auf jedes Wort zu verwenden wäre. Also, wenn Sie ...

std::vector<uint16_t> storage(n);  // where n is the number to be converted

// the following would do the trick
std::transform(word_storage.cbegin(), word_storage.cend()
  , word_storage.begin(), [](const uint16_t input)->uint16_t {
  return htons(input); });

Das oben wäre ein no-op, wenn Sie auf einem Big-Endian-System sind, so würde ich nach, was auch immer Ihre Plattform nutzt als Kompilierung-Zustand, um zu entscheiden, ob htons ist ein No-op. Es ist O (n), nachdem alle. Auf einem Mac wäre es so etwas wie ...

sein
#if (__DARWIN_BYTE_ORDER != __DARWIN_BIG_ENDIAN)
std::transform(word_storage.cbegin(), word_storage.cend()
  , word_storage.begin(), [](const uint16_t input)->uint16_t {
  return htons(input); });
#endif

Bitverschiebung nachschlagen, wie dies im Grunde alles, was Sie tun müssen, um von wenig zu tauschen -> Big-Endian. Dann abhängig von der Bit-Größe, die Sie ändern, wie Sie die Bitverschiebung tun.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top