Como faço para converter entre valores big-endian e little-endian em C ++?

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

  •  01-07-2019
  •  | 
  •  

Pergunta

Como faço para converter entre valores big-endian e little-endian em C ++?

EDIT: Para maior clareza, eu tenho que traduzir os dados binários (valores de ponto flutuante de precisão dupla e inteiros de 32-bit e 64-bit) de uma arquitetura de CPU para outra. Não se trata de rede, de modo ntoh () e funções similares não vai funcionar aqui.

EDIT # 2: A resposta que eu aceito se aplica diretamente a compiladores Eu estou com objectivo (que é por isso I escolheu). No entanto, há outras muito boas, respostas mais portáteis aqui.

Foi útil?

Solução

Se você estiver usando Visual C ++ faça o seguinte: Você incluir intrin.h e chamar as seguintes funções:

Para 16 números de bits:

unsigned short _byteswap_ushort(unsigned short value);

Para 32 números de bits:

unsigned long _byteswap_ulong(unsigned long value);

Para números de 64 bits:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8 números de bits (caracteres) não precisa ser convertido.

Também estes são apenas definido para valores não assinados que trabalham para inteiros assinados também.

Para carros alegóricos e duplos que é mais difícil, pois com inteiros simples como estas podem ou não estar no máquinas host byte ordem. Você pode obter carros alegóricos little-endian em máquinas big-endian e vice-versa.

Outros compiladores têm intrínsecos semelhantes também.

Em GCC por exemplo, você pode chamar diretamente:

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

(não há necessidade de incluir algo). Afaik bits.h declara a mesma função de uma forma não gcc-centric também.

16 bit troca é apenas um bit-rotação.

Chamando os intrínsecos em vez de rolar seus próprios dá-lhe a melhor densidade de desempenho e código btw ..

Outras dicas

Em termos simples:

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

uso:. swap_endian<uint32_t>(42)

De O Byte Order Falácia por Rob Pyke:

Vamos dizer que seu fluxo de dados tem um número inteiro de 32-bit little-endian-codificado. Veja como extraí-lo (assumindo que não assinados bytes):

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

Se é big-endian, aqui está como extraí-lo:

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

TL; DR:. Não se preocupe com a sua plataforma a fim nativa, tudo o que importa é a ordem de bytes do fluxo você está lendo a partir de, e é melhor esperar que ele está bem definido

Nota: foi observado no comentário que ausente explícita conversão de tipo, era importante que data ser uma matriz de unsigned char ou uint8_t. Usando signed char ou char (se assinado) resultará em data[x] sendo promovido para um inteiro e data[x] << 24 potencialmente mudando a 1 para o bit de sinal que é UB.

Se você está fazendo isso para fins de compatibilidade de rede / host que você deve usar:

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)

Se você está fazendo isso por algum outro motivo uma das soluções byte_swap apresentado aqui iria funcionar muito bem.

Eu levei algumas sugestões deste post e colocá-los juntos para formar o seguinte:

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

Há uma instrução de montagem chamado bswap que irá fazer a troca para você, extremamente rápida . Você pode ler sobre ele aqui .

Visual Studio, ou mais precisamente o Visual C ++ biblioteca de tempo de execução, tem intrínsecos plataforma para isso, chamado _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Semelhante deve existir para outras plataformas, mas eu não estou ciente de que eles seriam chamados.

O procedimento para passar de big-endian para little-endian é o mesmo que ir de little-endian para big-endian.

Aqui está um código de exemplo:

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

Nós fizemos isso com modelos. Você poderia então algo como isto:

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

Se você está fazendo isso para transferir dados entre diferentes plataformas olhar para as funções ntoh e hton.

Da mesma forma que você faz em C:

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

Você também pode declarar um vetor de caracteres não assinados, memcpy o valor de entrada para ele, reverter os bytes em outro vector e memcpy os bytes, mas que vai levar ordens de magnitude maior do que bit-twiddling, especialmente com 64- bit valores.

Na maioria dos sistemas POSIX (por isso não está no padrão POSIX) há a endian.h, que pode ser usado para determinar o que codificar os seus usos do sistema. De lá, é algo como isto:

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

Este swaps a ordem (de big-endian para little endian):

Se você tem o número 0xDEADBEEF (em um pequeno sistema endian armazenado como 0xEFBEADDE), ptr [0] será 0xEF, ptr [1] é 0xBE, etc.

Mas se você quiser usá-lo para a rede, então htons, htonl e htonll (e seus ntohs inversas, ntohl e ntohll) será útil para conversão de fim de acolhimento ordem de rede.

Note que, pelo menos para Windows, htonl () é muito mais lento do que o seu homólogo _byteswap_ulong intrínseca (). O primeiro é uma chamada de biblioteca DLL em ws2_32.dll, este último é uma instrução de montagem bswap. Portanto, se você está escrevendo algum código dependente de plataforma, preferem usar os intrínsecos para a velocidade:

#define htonl(x) _byteswap_ulong(x)

Isto pode ser especialmente importante para processamento de imagens PNG, onde todos os inteiros são salvos em Big Endian com a explicação "Pode-se usar htonl () ..." {a abrandar programas típicos do Windows, se você não está preparado}.

A maioria das plataformas tem um arquivo de cabeçalho do sistema que fornece funções byteswap eficientes. No Linux está em <endian.h>. Você pode envolvê-lo muito bem em 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';
}

Output:

cafe
deadbeaf
feeddeafbeefcafe

i como este, só por estilo: -)

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

A sério ... Eu não entendo por que todas as soluções são de que complicado ! Como sobre a função de modelo mais simples, mais geral que swaps qualquer tipo de qualquer tamanho em qualquer circunstância, em qualquer sistema operacional ????

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

É o poder mágico de C e C ++ juntos! Simplesmente trocar o caráter variável original pelo caráter.

Lembre-se que eu não usar o operador de atribuição simples "=" porque alguns objetos serão confuso quando a ordenação é invertida e o construtor de cópia (ou operador de atribuição) não funcionará. Portanto, é mais confiável para copiá-los carbonizar por Char.

Para chamá-lo, basta usar

double x = 5;
SwapEnd(x);

e agora x é diferente em endianness.

Eu tenho esse código que me permitem converter de HOST_ENDIAN_ORDER (seja ele qual for) para LITTLE_ENDIAN_ORDER ou BIG_ENDIAN_ORDER. Eu uso um modelo, então se eu tentar converter de HOST_ENDIAN_ORDER para LITTLE_ENDIAN_ORDER e acontecer de ser o mesmo para a máquina para wich eu compilar, nenhum código será gerado.

Aqui está o código com alguns comentários:

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

Se um big-endian 32 bits olhares inteiros não assinados como 0xAABBCCDD que é igual a 2864434397, então, que mesmo 32 bits olhares inteiros não assinados como 0xDDCCBBAA em um processador little-endian que também é igual a 2864434397.

Se um big-endian 16 bits olhares curtos sem sinal como 0xAABB que é igual a 43.707, então, que mesmo 16 bits olhares curtos sem sinal como 0xBBAA em um processador little-endian que também é igual a 43.707.

Aqui estão um par de funções #define acessível para trocar bytes little-endian para big-endian e 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))

Aqui está uma versão generalizada eu vim com em cima da minha cabeça, para trocar um valor no lugar. As outras sugestões seria melhor se o desempenho for um problema.

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

Disclaimer:. Eu não tentei compilar este ou teste ainda

Se você tomar o padrão comum para inverter a ordem de bits em uma palavra, e abater a parte que inverte os bits dentro de cada byte, então você é deixado com algo que só inverte os bytes dentro de uma palavra. Para 64 bits:

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

O compilador deve limpa as operações de mascaramento de bits supérfluos (Deixei-os para realçar o padrão), mas se isso não acontecer, você pode reescrever a primeira linha desta forma:

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

Isso normalmente deve simplificar-se a uma única instrução rodar na maioria das arquiteturas (ignorando que toda a operação é provavelmente uma instrução).

Em um processador RISC os grandes, constantes complicadas podem causar as dificuldades do compilador. Você pode trivialmente calcular cada uma das constantes da anterior, no entanto. Como assim:

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

Se você gosta, você pode escrever isso como um loop. Não vai ser eficiente, mas apenas para se divertir:

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

E para completar, aqui está a versão de 32 bits simplificado da primeira forma:

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

Apenas pensei que eu adicionei a minha própria solução aqui desde que eu não vi em qualquer lugar. É um pequeno e portátil C ++ função templated e portátil que apenas usos mordeu operações.

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

Com os códigos abaixo dadas você pode trocar entre bigEndian e littleEndian facilmente

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

Eu estou realmente surpreso que ninguém mencionou funções htobeXX e betohXX. Eles são definidos no endian.h e são muito semelhantes às funções de rede htonXX.

Uau, eu não podia acreditar algumas das respostas que eu li aqui. Na verdade, há uma instrução em assembly que faz isso mais rápido do que qualquer outra coisa. bswap. Você poderia simplesmente escrever uma função como esta ...

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

É MUITO mais rápido do que os intrínsecos que têm sido sugeridas. Eu desmontada los e olhou. A função acima não tem prólogo / epílogo assim praticamente não tem nenhuma sobrecarga de todo.

unsigned long _byteswap_ulong(unsigned long value);

Fazendo 16 bit é tão fácil, com a exceção de que você usaria al xchg, ah. bswap só funciona em registros de 32 bits.

64-bit é um pouco mais complicado, mas sem exagero. Muito melhor do que todos os exemplos acima com loops e modelos etc.

Existem algumas ressalvas aqui ... Em primeiro lugar bswap só está disponível em 80x486 CPU e acima. É o planejamento qualquer um em executá-lo em um 386?!? Se assim for, você ainda pode substituir bswap com ...

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

Também está em linha de montagem só está disponível em código x86 no Visual Studio. A função de nu não pode ser alinhada e também não está disponível em x64 constrói. I essa instância, você vai ter que usar os intrínsecos do compilador.

técnica portátil para implementar non-inplace acessores endian não alinhados amigável-Optimizer. Eles trabalham em cada compilador, cada alinhamento limite e cada ordenação de bytes. Estas rotinas desalinhadas são completadas, ou sugerida, dependendo endian nativo e alinhamento. Parcial lista mas você começa a idéia. BO * são valores constantes com base na ordenação de bytes nativa.

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

Estes typedefs têm o benefício de levantar erros do compilador se não for usado com assessores, mitigando assim esquecido acessor bugs.

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

Eu escrevi recentemente uma macro para fazer isso em C, mas é igualmente válida em 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)

Ele aceita qualquer tipo e inverte os bytes no argumento passado. Exemplo usos:

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

Que impressões:

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

A descrição acima é perfeitamente copiar / colar-capazes, mas há muita coisa acontecendo aqui, então eu vou quebrar como ele funciona peça por peça:

A primeira coisa notável é que toda a macro é envolto em um bloco do while(0). Esta é uma comum idioma para permitir o uso normal ponto e vírgula após a macro.

Em seguida é o uso de uma variável denominada REVERSE_BYTES como balcão do circuito for. O próprio nome da macro é usado como um nome de variável para garantir que ele não colidir com quaisquer outros símbolos que podem estar no escopo onde a macro é usado. Uma vez que o nome está sendo usado dentro de expansão da macro, não será ampliado novamente quando usado como um nome de variável aqui.

Dentro do loop for, existem dois bytes a ser referenciado e XOR trocadas (assim um nome da variável temporária não é necessária):

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

__VA_ARGS__ representa tudo o que foi dado para a macro, e é usado para aumentar a flexibilidade do que pode ser passado (embora não por muito). O endereço do argumento é então tomada e fundido a um ponteiro unsigned char para permitir a troca de seus bytes através subscripting matriz [].

O ponto peculiar final é a falta de chaves {}. Eles não são necessários porque todas as etapas em cada troca são unidas com a vírgula operador , tornando-os um comunicado.

Finalmente, é importante notar que esta não é a abordagem ideal se a velocidade é uma prioridade. Se este é um fator importante, alguns dos macros específicas do tipo ou directivas específicas de plataforma referenciados em outras respostas são provavelmente uma opção melhor. Esta abordagem, no entanto, é portátil para todos os tipos, todas as principais plataformas, e ambas as linguagens C e C ++.

Tente Boost::endian, e não se implementá-lo!

Aqui está um link

Veja como ler um double armazenado em IEEE 754 formato de 64 bits, mesmo se o computador host usa um sistema diferente.

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

Para o resto do conjunto de funções, incluindo a escrita e as rotinas inteiros ver meu projeto github

https://github.com/MalcolmMcLean/ieee754

Byte troca com Ye Olde truque 3-step-xor em torno de um pivô em uma função de modelo dá uma O (LN2) solução flexível e rápida que não requer uma biblioteca, o estilo aqui também rejeita 1 tipos de bytes:

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

Parece que a maneira segura seria usar htons em cada palavra. Então, se você tem ...

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

O acima seria um não-op se você estivesse em um sistema big-endian, então eu olharia para o que seus usos plataforma como uma condição de tempo de compilação para decidir se htons é um não-op. É O (n) depois de tudo. Em um Mac, seria algo como ...

#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

Olhe para cima deslocamento de bits, como este é basicamente tudo que você precisa fazer para troca de pouco -> big endian. Em seguida, dependendo do tamanho do bit, você mudar a forma como você faz o deslocamento de bits.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top