Jak konwertować wartości big-endian i little-endian w C++?

Jak konwertować wartości big-endian i little-endian w C++?

EDIT: dla jasności, muszę przetłumaczyć dane binarne (wartości zmiennoprzecinkowe o podwójnej precyzji oraz 32-bitowe i 64-bitowe liczby całkowite) z jednej architektury procesora na drugą. Nie wiąże się to z siecią, więc ntoh () i podobne funkcje nie będą tutaj działać.

EDIT # 2: odpowiedź, którą zaakceptowałem dotyczy bezpośrednio kompilatorów, które targetuję (dlatego ją wybrałem). Istnieją jednak inne bardzo dobre, bardziej przenośne odpowiedzi tutaj.

Author: Yu Hao, 2008-09-20

28 answers

Jeśli używasz Visual C++ wykonaj następujące czynności: Dołącz intrin.h i wywołują następujące funkcje:

Dla liczb 16-bitowych:

unsigned short _byteswap_ushort(unsigned short value);

Dla liczb 32-bitowych:

unsigned long _byteswap_ulong(unsigned long value);

Dla liczb 64-bitowych:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8 bitowe liczby (znaki) nie muszą być konwertowane.

Są również zdefiniowane tylko dla niepodpisanych wartości, które działają również dla podpisanych liczb całkowitych.

Dla pływaków i dublerów jest trudniej jak dla zwykłych liczb całkowitych, ponieważ mogą one być lub nie w kolejność bajtów maszyn hosta. Można dostać little-endian pływaki na maszynach big-endian i vice versa.

Inne Kompilatory również mają podobne cechy wewnętrzne.

W GCC na przykład możesz bezpośrednio wywołać:

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

(nie ma potrzeby dołączania czegoś). AFAIK bits.h deklaruje tę samą funkcję również w sposób nie GCC-centryczny.

16 bit swap to tylko bit-rotate.

Wywołanie intrinsics zamiast toczenia własnych daje najlepszą wydajność i Kod gęstość btw..

 140
Author: Nils Pipenbrinck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 20:36:44

Po Prostu:

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

Użycie: swap_endian<uint32_t>(42).

 68
Author: Alexandre C.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-06 19:55:55

Od błąd kolejności bajtów autor: Rob Pyke:

Powiedzmy, że twój strumień danych ma 32-bitową liczbę całkowitą. Oto jak go wyodrębnić (zakładając niepodpisane bajty):
i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

Jeśli jest big-endian, oto jak go wyodrębnić:

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

TL; DR: nie martw się o natywną kolejność platformy, liczy się tylko kolejność bajtów strumienia, z którego czytasz, i lepiej miej nadzieję, że jest dobrze zdefiniowany.

Uwaga: w komentarzu zauważono, że bez jawnej konwersji typów ważne było, aby data Była tablicą unsigned char lub uint8_t. Użycie signed char lub char (jeśli jest podpisane) spowoduje, że data[x] zostanie promowana do liczby całkowitej i data[x] << 24 potencjalnie przesunie 1 do bitu znaku, który jest UB.

 58
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-10 19:53:52

Jeśli robisz to dla celów kompatybilności sieci / hosta, powinieneś użyć:

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)

Jeśli robisz to z jakiegoś innego powodu, jedno z przedstawionych tutaj rozwiązań byte_swap będzie działać dobrze.

 47
Author: Frosty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 20:38:18

Wzięłam kilka sugestii z tego postu i poskładałam je w całość:

#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);
}
 25
Author: Steve Lorimer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-19 14:36:13

Istnieje instrukcja montażu o nazwie BSWAP, która wykona zamianę za ciebie, niezwykle szybko . Możesz o tym przeczytać tutaj .

Visual Studio, a ściślej Visual C++ runtime library, posiada wbudowaną platformę o nazwie _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Podobne powinny istnieć dla innych platform, ale nie jestem świadomy, jak by się nazywały.

 15
Author: anon6439,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 20:34:46

Procedura przechodzenia z big-endian do little-endian jest taka sama jak przechodzenie z little-endian do big-endian.

Oto przykładowy kod:

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);
}
 12
Author: Kevin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-11 04:27:38

Zrobiliśmy to za pomocą szablonów. Można tak coś takiego:

// 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);
}
 11
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 20:29:23

Jeśli robisz to, aby przesyłać dane między różnymi platformami, spójrz na funkcje ntoh i hton.

 8
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 20:26:48

Tak samo jak w C:

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

Można również zadeklarować wektor znaków niepodpisanych, memcpy wartość wejściową do niego, odwrócić bajty do innego wektora i memcpy bajty na zewnątrz, ale to zajmie rząd wielkości dłużej niż bit-twidling, zwłaszcza z wartościami 64-bitowymi.

 6
Author: Ben Straub,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 20:30:07

W większości systemów POSIX (przez to, że nie jest w standardzie POSIX) istnieje endian.h, który może być użyty do określenia, jakiego kodowania używa Twój system. Stamtąd jest coś takiego:

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

To zmienia kolejność (z big-endian na little endian):

Jeśli masz liczbę 0xDEADBEEF (na małym endiańskim systemie zapisaną jako 0xefbeadde), ptr [0] będzie 0xEF, ptr[1] będzie 0xbe, itd.

Ale jeśli chcesz go użyć do sieci, to htons, htonl i htonll (i ich odwrotne ntohs, ntohl i ntohll) będą pomocne w konwersji z kolejności hosta do kolejności sieciowej.

 6
Author: terminus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-01 17:30:23

Zauważ, że przynajmniej dla Windows, htonl() jest znacznie wolniejsze niż ich wewnętrzny odpowiednik _byteswap_ulong(). Pierwsze jest wywołaniem biblioteki DLL do ws2_32.dll, ten ostatni jest jedną instrukcją montażu BSWAP. Dlatego, jeśli piszesz kod zależny od platformy, preferuj użycie intrinsics for speed:

#define htonl(x) _byteswap_ulong(x)
To może być szczególnie ważne .PNG image processing where all integers are saved in Big Endian with explanation " można użyć htonl ()..."{spowolnić typowy Programy Windows, jeśli nie jesteś przygotowany}.
 5
Author: user2699548,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-20 10:45:38

Większość platform ma Systemowy plik nagłówkowy, który zapewnia wydajne funkcje bajtowe. Na Linuksie jest w <endian.h>. Można to ładnie zapakować w C++:

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

Wyjście:

cafe
deadbeaf
feeddeafbeefcafe
 4
Author: Maxim Egorushkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-07 18:02:01

Podoba mi się ten, tylko dla stylu: -)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
 4
Author: friedemann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-26 22:13:17

Poważnie... Nie rozumiem dlaczego wszystkie rozwiązania są takie skomplikowane! a co powiesz na najprostszą, najbardziej ogólną funkcję szablonu, która zamienia dowolny typ dowolnego rozmiaru w dowolnym systemie operacyjnym????

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]);
}
To magiczna moc C i c++ razem! Po prostu zamień oryginalną zmienną znak po znaku.

Pamiętaj, że nie użyłem prostego operatora przypisania"=", ponieważ niektóre obiekty będą pomieszane, gdy endianness jest odwrócony i Konstruktor kopiujący (lub operator przypisania) nie będzie działał. Dlatego bardziej niezawodne jest kopiowanie ich znak po znaku.

Aby go nazwać, wystarczy użyć

double x = 5;
SwapEnd(x);

A teraz x jest inaczej w endianess.

 4
Author: The Quantum Physicist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-29 16:12:25

Mam ten kod, który pozwala mi przekonwertować z HOST_ENDIAN_ORDER (cokolwiek to jest) na LITTLE_ENDIAN_ORDER lub BIG_ENDIAN_ORDER. Używam szablonu, więc jeśli spróbuję przekonwertować z HOST_ENDIAN_ORDER na LITTLE_ENDIAN_ORDER i będą one takie same dla maszyny, dla której kompiluję, żaden kod nie zostanie wygenerowany.

Oto kod z kilkoma komentarzami:

// 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);
}
 3
Author: Mathieu Pagé,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-20 04:25:11

Jeśli 32-bitowa niepodpisana liczba całkowita big-endian wygląda jak 0xAABBCCDD, która jest równa 2864434397, to ta sama 32-bitowa niepodpisana liczba całkowita wygląda jak 0xddccbbaa na procesorze little-endian, który jest również równy 2864434397.

Jeśli 16-bitowy niepodpisany Skrót big-endian wygląda jak 0xaabb, który jest równy 43707, to ten sam 16-bitowy niepodpisany Skrót wygląda jak 0xBBAA na procesorze little-endian, który jest również równy 43707.

Oto kilka przydatnych funkcji # define do zamiany bajty od little-endian do big-endian i vice-versa -- >

// 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))
 3
Author: Adam Freeman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-06 22:35:58

Oto uogólniona wersja, którą wymyśliłem z czubka głowy, za zamianę wartości na miejsce. Inne sugestie byłyby lepsze, gdyby wydajność była problemem.

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

Zastrzeżenie: nie próbowałem jeszcze skompilować tego lub przetestować.

 2
Author: Mark Ransom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-21 14:08:34

Jeśli weźmiesz wspólny wzorzec odwracania kolejności bitów w słowie i usuniesz część, która odwraca bity w każdym bajcie, pozostaniesz z czymś, co odwraca tylko bajty w słowie. Dla 64-bitów:

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

Kompilatorpowinien wyczyścić zbędne operacje maskowania bitów( zostawiłem je, aby podświetlić wzorzec), ale jeśli nie, możesz przepisać pierwszą linię w ten sposób:

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

Które zwykle powinny uprościć do jednego obrotu instrukcja na większości architektur (ignorując, że cała operacja jest prawdopodobnie jedną instrukcją).

Na procesorze RISC duże, skomplikowane stałe mogą powodować trudności kompilatora. Można jednak trywialnie obliczyć każdą ze stałych z poprzedniej. Tak:

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

Jeśli chcesz, możesz to zapisać jako pętlę. To nie będzie skuteczne, ale tylko dla Zabawy:

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

I dla kompletności, oto uproszczona 32-bitowa wersja pierwszego forma:

x = ( x               << 16) ^  (x >> 16);
x = ((x & 0x00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff);
 2
Author: sh1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-20 11:51:33

Pomyślałem, że dodałem własne rozwiązanie, ponieważ nigdzie go nie widziałem. Jest to mała i przenośna funkcja szablonów C++ i portable, która używa tylko operacji bitowych.

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;
}
 1
Author: Joao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-29 19:30:25

Z poniższymi kodami możesz łatwo zamienić BigEndian na LittleEndian

#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))
 1
Author: Pzy64,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-17 15:58:44

Jestem naprawdę zaskoczony, że nikt nie wspomniał o funkcjach htobeXX i betohXX. Są one zdefiniowane w endian.h i są bardzo podobne do funkcji sieciowych htonXX.

 1
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-11 19:15:28

Wow, nie mogłem uwierzyć w niektóre odpowiedzi, które tu przeczytałem. W montażu jest instrukcja, która robi to szybciej niż cokolwiek innego. bswap. Możesz po prostu napisać taką funkcję...

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

Jest to znacznie szybsze niż sugerowano. Zdemontowałem je i spojrzałem. Powyższa funkcja nie ma prologu/epilogu, więc praktycznie nie ma narzutu.

unsigned long _byteswap_ulong(unsigned long value);

Robienie 16 bitów jest równie proste, z tym wyjątkiem, że użyj xchg al, ah. bswap działa tylko na rejestrach 32-bitowych.

64-bit jest trochę bardziej skomplikowany, ale nie przesadnie. Znacznie lepiej niż wszystkie powyższe przykłady z pętlami i szablonami itp.

Są tu pewne zastrzeżenia... Po pierwsze bswap jest dostępny tylko na procesorach 80X486 i wyższych. Czy ktoś planuje uruchomić go na 386?!? Jeśli tak, nadal możesz zastąpić bswap...
mov ebx, eax
shr ebx, 16
xchg bl, bh
xchg al, ah
shl eax, 16
or eax, ebx

Również inline assembly jest dostępny tylko w kodzie x86 w Visual Studio. Naga funkcja nie może być lined, a także nie jest dostępny w kompilacjach x64. W tym przykładzie będziesz musiał użyć kompilatora.

 0
Author: The Welder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-24 01:47:38

Przenośna technika implementacji przyjaznych optymalizatorowi, niepodpisanych nie-inplace endian accesorów. Działają one na każdym kompilatorze, każdym wyrównaniu granic i każdym porządkowaniu bajtów. Te niezaliczone procedury są uzupełniane lub mooted, w zależności od natywnego endian i alignment. Częściowa lista, ale masz pomysł. BO * są stałymi wartościami bazującymi na natywnej kolejności bajtów.

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

Te typy mają tę zaletę, że generują błędy kompilatora, jeśli nie są używane z accesorami, co zmniejsza zapomniane błędy dostępu.

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 */
 0
Author: BSalita,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-23 08:53:38

Ostatnio napisałem makro, aby to zrobić w C, ale jest to równie ważne w 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)

Przyjmuje dowolny typ i odwraca bajty w przekazanym argumencie. Przykładowe zastosowania:

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

Który drukuje:

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

Powyższe jest doskonale copy / paste-able, ale dużo się tu dzieje, więc opiszę jak to działa kawałek po kawałku:

Pierwszą godną uwagi rzeczą jest to, że całe makro jest zamknięte w bloku do while(0). To jest wspólny idiom, aby umożliwić zwykłe użycie średnika po makrze.

Następnym krokiem jest użycie zmiennej o nazwie REVERSE_BYTES jako licznika pętli for. Nazwa samego makra jest używana jako nazwa zmiennej, aby upewnić się, że nie koliduje ono z innymi symbolami, które mogą znajdować się w zasięgu wszędzie tam, gdzie makro jest używane. Ponieważ nazwa jest używana w rozszerzeniu makra, nie zostanie ona ponownie rozszerzona, gdy zostanie użyta jako nazwa zmiennej.

Wewnątrz pętli for znajdują się dwa bajty odwołujące i XOR zamienione (więc tymczasowa nazwa zmiennej nie jest wymagana):

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

__VA_ARGS__ reprezentuje to, co zostało dane makro, i jest używany do zwiększenia elastyczności tego, co może zostać przekazane (choć nie przez wiele). Adres tego argumentu jest następnie pobierany i przesyłany do wskaźnika unsigned char, aby umożliwić zamianę jego bajtów za pomocą tablicy [] subscripting.

Ostatnim osobliwym punktem jest brak {} szelek. Nie są konieczne, ponieważ wszystkie kroki w każdej wymianie są połączone z operatorem przecinka , czyniąc je jednym stwierdzeniem.

Na koniec warto zauważyć, że nie jest to idealne podejście, jeśli szybkość jest priorytetem. Jeśli jest to ważny czynnik, lepszym rozwiązaniem są niektóre makra specyficzne dla typu lub dyrektywy specyficzne dla platformy, o których mowa w innych odpowiedziach. Takie podejście jest jednak przenośne dla wszystkich typów, wszystkich głównych platform, a także języków C i C++.

 0
Author: Ryan Hilbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-17 07:27:03

Oto jak odczytać podwójny zapis w formacie IEEE 754 64 bit, nawet jeśli komputer hosta używa innego systemu.

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

Reszta pakietu funkcji, w tym procedury write I integer, znajduje się w moim projekcie github

Https://github.com/MalcolmMcLean/ieee754

 0
Author: Malcolm McLean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-22 22:40:33

Spróbuj Boost::endian i nie wdrażaj go samodzielnie!

Oto link

 -1
Author: morteza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-22 22:16:19

Poszukaj zmiany bitów, ponieważ jest to w zasadzie wszystko, co musisz zrobić, aby zamienić się z little - > big endian. Następnie w zależności od rozmiaru bitu zmieniasz sposób przesuwania bitów.

 -4
Author: Redbaron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 20:30:37