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

196

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

EDIT: Para maior clareza, eu tenho que converter dados binários (valores de ponto flutuante de precisão dupla e números inteiros de 32 e 64 bits) de uma arquitetura de CPU para outra. Isso não envolve redes, portanto, ntoh () e funções semelhantes não funcionarão aqui.

EDIÇÃO 2: A resposta que aceitei se aplica diretamente aos compiladores que estou segmentando (e foi por isso que a escolhi). No entanto, existem outras respostas muito boas e mais portáteis aqui.

Uhall
fonte
21
O ntoh hton funcionará bem, mesmo que não tenha nada a ver com redes.
Ben Collins
2
A melhor maneira de lidar com endianness em geral é garantir que o código seja executado em máquinas host pequenas e grandes. Se isso funcionar, você provavelmente fez o certo. Assumir que você esteja no x86 / be é perigoso como prática.
jakobengblom2
10
O hton ntoh não funcionará se a máquina for big-endian, porque o solicitante de pergunta deseja explicitamente realizar a conversão.
Fabspro #
6
@ jakobengblom2 é a única pessoa a mencionar isso. Quase todos os exemplos desta página usam conceitos como bytes "swap" em vez de agnósticos da endianidade subjacente. Se você estiver lidando com formatos de arquivo externos (que possuem endianness bem definido), a coisa mais portátil a fazer é tratar os dados externos como um fluxo de bytes e converter o fluxo de bytes de e para os números inteiros nativos. Eu me arrepio toda vez que vejo o short swap(short x)código, pois ele será quebrado se você passar para uma plataforma com endianness diferente. Matthieu M tem a única resposta certa abaixo.
precisa saber é o seguinte
3
Você está pensando sobre o problema completamente errado. A tarefa não é "como faço para converter valores big-endian e little-endian". A tarefa é "como converter valores de ponto flutuante e número inteiro em um formato específico para o formato nativo da minha plataforma". Se você fizer isso corretamente, o formato nativo pode ser big endian, little endian, misto endian ou ternário para todos os seus cuidados com o código.
David Schwartz

Respostas:

166

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

Para números de 16 bits:

unsigned short _byteswap_ushort(unsigned short value);

Para números de 32 bits:

unsigned long _byteswap_ulong(unsigned long value);

Para números de 64 bits:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

Números de 8 bits (caracteres) não precisam ser convertidos.

Além disso, eles são definidos apenas para valores não assinados e também funcionam para números inteiros assinados.

Para carros alegóricos e duplos, é mais difícil, pois com números inteiros simples, pois podem ou não estar na ordem de bytes das máquinas host. Você pode obter carros alegóricos little-endian em máquinas big-endian e vice-versa.

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

No GCC, por exemplo, você pode chamar diretamente alguns recursos internos conforme documentado aqui :

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

(não é necessário incluir algo). O Afaik bits.h também declara a mesma função de maneira não centrada no GCC.

A troca de 16 bits é apenas uma rotação de bits.

Chamar os intrínsecos em vez de criar o seu próprio fornece a você o melhor desempenho e densidade de código entre.

Nils Pipenbrinck
fonte
11
Com GCC, eu poderia usar: #include <byteswap.h> int32_t bswap_32 (int32_t x) int64_t bswap_64 (int64_t x)
jmanning2k
5
__builtin_bswapXsó está disponível a partir de 4,3-GCC em diante
Matt Joiner
20
Também é importante notar que esses bytes intrínsecos / sempre / trocados, eles não são como htonl,htons etc. Você tem que saber a partir do contexto de sua situação quando realmente trocar os bytes.
Brian Vandenberg
8
@ Jason, porque os números de 8 bits são os mesmos em grande e pequeno endian. :-)
Nils Pipenbrinck
2
@BrianVandenberg Right; usar htonle ntohlsem se preocupar com o contexto funcionaria ao escrever código portátil, já que a plataforma que define essas funções o trocaria se fosse pouco / mid-endian e, no big-endian, não funcionaria. No entanto, ao decodificar um tipo de arquivo padrão definido como little-endian (por exemplo, BMP), ainda é necessário conhecer o contexto e não podemos apenas confiar em htonle ntohl.
legends2k
86

Simplificando:

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

Alexandre C.
fonte
3
Tenha um voto positivo. Acabei de usar uchars e atribui 4 a 1, 3 a 2, 2 a 3 e 1 a 4, mas isso é mais flexível se você tiver tamanhos diferentes. 6 relógios em um 1st Gen Pentium IIRC. O BSWAP é de 1 relógio, mas é específico da plataforma.
2
@RocketRoy: Sim, e se a velocidade se tornar um problema, é muito simples escrever sobrecargas com detalhes específicos da plataforma e do tipo.
Alexandre C.
3
@ MihaiTodor: Esse uso de uniões para tipografia através de uma variedade de caracteres é explicitamente permitido pelo padrão. Veja por exemplo. esta pergunta .
Alexandre C.
4
@AlexandreC. Não no padrão C ++ - somente no C. No C ++ (que é esse código), esse código é um comportamento indefinido.
Rapptz
4
@Rapptz: 3.10 parece claro: "Se um programa tentar acessar o valor armazenado de um objeto por meio de um valor gl gl diferente de um dos seguintes tipos, o comportamento será indefinido: [...] um caractere ou um tipo de caracter não assinado. ". Talvez esteja faltando alguma coisa aqui, mas estava bem claro para mim que acessar qualquer tipo através de ponteiros de char era explicitamente permitido.
Alexandre C.
75

De falácia da ordem de bytes de Rob Pike:

Digamos que seu fluxo de dados tenha um número inteiro de 32 bits codificado em endian. Veja como extraí-lo (assumindo bytes não assinados):

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

Se for big-endian, veja como extraí-lo:

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

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

Nota: foi observado no comentário que, na ausência de conversão explícita de tipo, era importante que datafosse uma matriz de unsigned charou uint8_t. Usar signed charou char(se assinado) resultará na data[x]promoção para um número inteiro e, data[x] << 24potencialmente, na mudança de 1 para o bit de sinal que é UB.

Matthieu M.
fonte
5
Isso é legal, mas me parece que isso se aplica apenas a números inteiros e variantes. O que fazer com carros alegóricos / duplos?
Brett
1
@ v.oddou: sim e não, os arquivos mapeados na memória são exatamente iguais aos quadros de rede; se você aceitar não lê-los diretamente, tudo o que importa é a sua persistência: se é pouco endian, use a primeira fórmula; se for big-endian, use a segunda. Qualquer compilador que se preze otimizará as transformações desnecessárias se a endianidade corresponder.
precisa
2
@ meowsqueak: Sim, eu esperaria que funcionasse, porque apenas a ordem dos bytes muda, não a ordem dos bits em cada byte.
Matthieu M.
3
Em uma nota pouco relacionada, o post vinculado é uma leitura desagradável ... O cara parece valorizar a brevidade, mas preferiu escrever um longo discurso sobre todos os maus programadores que não são tão esclarecidos quanto ele em relação à endianidade, em vez de realmente explicando a situação e POR QUE sua solução sempre funciona.
Ad N
1
Se você estiver usando esse método, certifique-se de transmitir seus dados para (não assinado *)
joseph
51

Se você estiver fazendo isso para fins de compatibilidade de rede / host, deverá 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ê estiver fazendo isso por algum outro motivo, uma das soluções byte_swap apresentadas aqui funcionaria perfeitamente.

Gelado
fonte
2
ordenação de bytes de rede é grande endian eu acredito. Essas funções podem ser usadas com isso em mente, mesmo se você não estiver usando o código de rede. Entretanto, não há versões flutuador ntohf ou htonf
Matt
2
Matt H., isso é quase sempre correto. Nem todos os sistemas de computador têm ordem de bytes little-endian. Se você estava trabalhando, digamos um motorolla 68k, um PowerPC ou outra arquitetura big endian, essas funções não trocam bytes, porque elas já estão na ordem de 'bytes de rede.
Frosty
2
Infelizmente, htonle ntohlnão pode ir para little endian em uma plataforma big-endian.
Brian Vandenberg
2
@celtschk, entendido; no entanto, o OP quer uma maneira de mudar o endianness, mesmo em um ambiente big endian.
22413 Brian Vandenberg
4
Para evitar a inevitável questão: existem várias razões para precisar de LE para uma plataforma de BE; vários formatos de arquivo (bmp, fli, pcx, qtm, rtf, tga para citar alguns) usam pequenos valores endian ... ou, pelo menos, algumas versões do formato usavam ao mesmo tempo.
22713 Brian Vandenberg
26

Peguei algumas sugestões deste post e as reuni para formar isso:

#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);
}
Steve Lorimer
fonte
você também deve incluir <cstdint> ou <stdint.h>, por exemplo, para uint32_t
ady
17

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

Aqui está um exemplo de código:

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);
}
Kevin
fonte
2
A última função postada aqui está incorreta e deve ser editada para: void swapByteOrder (sem assinatura, long long & ull) {ull = (ull >> 56) | ... (ull << 56); }
Eric Burnett
14
Eu não acho correto usar lógico-e (&&) em oposição a bit a bit-e (&). De acordo com a especificação C ++, ambos os operandos são implicitamente convertidos em bool, o que não é o que você deseja.
Trevor Robinson
16

Existe uma instrução de montagem chamada BSWAP que fará a troca para você, extremamente rápido . Você pode ler sobre isso aqui .

O Visual Studio, ou mais precisamente a biblioteca de tempo de execução do Visual C ++, possui intrínsecos de plataforma para isso, chamados _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Semelhante deve existir para outras plataformas, mas não sei como elas seriam chamadas.

anon6439
fonte
Esse é um ótimo link. Isso reacendeu meu interesse no montador x86.
PP.
1
Os resultados de tempo do BSWAP são apresentados aqui. gmplib.org/~tege/x86-timing.pdf ... e aqui ... agner.org/optimize/instruction_tables.pdf
12

Fizemos isso com modelos. Você poderia fazer algo assim:

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

Se você estiver fazendo isso para transferir dados entre plataformas diferentes, observe as funções ntoh e hton.

Andrew
fonte
7

Da mesma maneira 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, digitar o valor de entrada incorreto, inverter os bytes em outro vetor e digitar os bytes, mas isso exigirá ordens de magnitude maiores do que a manipulação de bits, especialmente com valores de 64 bits.

Ben Straub
fonte
7

Na maioria dos sistemas POSIX (por não estar no padrão POSIX), existe o endian.h, que pode ser usado para determinar qual codificação seu sistema usa. A partir daí, é algo como isto:

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

Isso troca a ordem (de big endian para little endian):

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

Mas se você quiser usá-lo para redes, htons, htonl e htonll (e seus inversos ntohs, ntohl e ntohll) serão úteis para a conversão de ordem de host em ordem de rede.

terminus
fonte
6
Isso é engraçado - o padrão POSIX em opengroup.org/onlinepubs/9699919799/toc.htm não menciona um cabeçalho '<endian.h> `.
Jonathan Leffler
1
Você pode usar htonle amigos, independentemente de o caso de uso ter algo a ver com a rede. A ordem dos bytes da rede é big-endian, portanto, trate essas funções como host_to_be e be_to_host. (Não ajuda se você precisar host_to_le, no entanto.)
Peter Cordes
5

Observe que, pelo menos no Windows, o htonl () é muito mais lento que o equivalente intrínseco _byteswap_ulong (). O primeiro é uma chamada de biblioteca DLL para o ws2_32.dll, o último é uma instrução de montagem BSWAP. Portanto, se você estiver escrevendo algum código dependente da plataforma, prefira usar os intrínsecos para obter velocidade:

#define htonl(x) _byteswap_ulong(x)

Isso pode ser especialmente importante para o processamento de imagens .PNG, onde todos os números inteiros são salvos no Big Endian com a explicação "Pode-se usar htonl () ..." {para diminuir a velocidade dos programas típicos do Windows, se você não estiver preparado}.

user2699548
fonte
4

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

Resultado:

cafe
deadbeaf
feeddeafbeefcafe
Maxim Egorushkin
fonte
Alteração: #define BYTESWAPS (bits) \ template <classe T> inline T htobe (T t, SizeT <bits / 8>) {return htobe ## bits (t); } \ template <classe T> inline T htole (T t, SizeT <bits / 8>) {retornar htole ## bits (t); } \ template <classe T> inline T betoh (Tt, SizeT <bits / 8>) {return be ## bits ## toh (t); } \ template <classe T> inline T letoh (T t, SizeT <bits / 8>) {return le ## bits ## toh (t); }
ldav1s 22/02
Obrigado, esqueci de testar betoh () e letoh ().
Maxim Egorushkin,
4

Eu gosto deste, apenas pelo estilo :-)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
friedemann
fonte
Eu recebo um erro ao char[]dizer 'Erro: tipo incompleto não é permitido'
Portland Runner
4

Sério ... Eu não entendo por que todas as soluções são tão complicadas ! Que tal a função de modelo mais simples e mais geral que troca qualquer tipo de tamanho sob qualquer circunstância em qualquer sistema operacional ????

template <typename T>
void SwapEnd(T& var)
{
    static_assert(std::is_pod<T>::value, "Type must be POD type for safety");
    std::array<char, sizeof(T)> varArray;
    std::memcpy(varArray.data(), &var, sizeof(T));
    for(int i = 0; i < static_cast<int>(sizeof(var)/2); i++)
        std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
    std::memcpy(&var, varArray.data(), sizeof(T));
}

É o poder mágico de C e C ++ juntos! Simplesmente troque a variável original caractere por caractere.

Ponto 1 : Sem operadores: lembre-se de que eu não usei o operador de atribuição simples "=" porque alguns objetos serão confusos quando a endianidade for invertida e o construtor de cópias (ou operador de atribuição) não funcionará. Portanto, é mais confiável copiá-los char por char.

Ponto 2 : Esteja ciente dos problemas de alinhamento: Observe que estamos copiando para e de uma matriz, o que é correto, porque o compilador C ++ não garante que possamos acessar a memória desalinhada (esta resposta foi atualizada a partir da original) formulário para isso). Por exemplo, se você alocar uint64_t, seu compilador não pode garantir que você possa acessar o terceiro byte dele como a uint8_t. Portanto, a coisa certa a fazer é copiar isso para uma matriz de caracteres, trocá-lo e copiá-lo novamente (então não reinterpret_cast). Observe que os compiladores são inteligentes o suficiente para converter o que você fez em reinterpret_castse eles são capazes de acessar bytes individuais, independentemente do alinhamento.

Para usar esta função :

double x = 5;
SwapEnd(x);

e agora xé diferente em endianness.

O físico quântico
fonte
2
Isto irá funcionar em qualquer lugar, mas a montagem OCDE produzido será muitas vezes abaixo do ideal: ver a minha pergunta stackoverflow.com/questions/36657895/...
j_kubik
Você usa new/ deletepara alocar um buffer para isso?!? sizeof(var)é uma constante em tempo de compilação, então você pode fazer char varSwapped[sizeof(var)]. Ou você pode fazer char *p = reinterpret_cast<char*>(&var)e trocar no local.
26817 Peter Cordes
@ Peter Esta resposta é rápida e suja para provar um argumento. Vou implementar suas sugestões. No entanto, você não precisa ser um mega SO AH e fazer o voto negativo da solução de 5 linhas em comparação com as soluções de 50 linhas fornecidas lá em cima. Eu não vou dizer mais nada.
A Quantum Físico
Essa resposta faz alguns pontos úteis sobre o cuidado com construtores e operadores sobrecarregados em dados incorretos, então ficaria feliz em remover meu voto negativo uma vez que o código não é horrível e é algo que um bom compilador pode compilar em um bswap instrução. Além disso, eu sugiro usar em for(size_t i = 0 ; i < sizeof(var) ; i++)vez de a static_cast<long>. (Ou, na verdade, a troca no local usará uma ascendente e uma descendente char*para que desapareça de qualquer maneira).
26517 Peter Cordes
por exemplo, veja a resposta de Mark Ransom usando std :: swap para reverter no local.
26817 Peter Cordes
3

Eu tenho esse código que me permite converter de HOST_ENDIAN_ORDER (o que for) para LITTLE_ENDIAN_ORDER ou BIG_ENDIAN_ORDER. Eu uso um modelo, portanto, se eu tentar converter de HOST_ENDIAN_ORDER para LITTLE_ENDIAN_ORDER e eles forem iguais para a máquina para a qual 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);
}
Mathieu Pagé
fonte
3

Se um número inteiro não assinado de 32 bits e big endian se parecer com 0xAABBCCDD, que é igual a 2864434397, esse mesmo número inteiro sem sinal de 32 bits se parecerá com 0xDDCCBBAA em um processador little endian, que também é igual a 2864434397.

Se um short não assinado de 16 bits e big endian se parece com 0xAABB, que é igual a 43707, esse mesmo short não assinado de 16 bits se parece com 0xBBAA em um processador little endian que também é igual a 43707.

Aqui estão algumas funções úteis #define para trocar bytes de 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))
Adam Freeman
fonte
2

Aqui está uma versão generalizada que me veio à cabeça, para trocar um valor no lugar. As outras sugestões seriam melhores 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]);
    }

Isenção de responsabilidade: ainda não tentei compilar ou testá-lo.

Mark Ransom
fonte
2

Se você usar o padrão comum para reverter a ordem dos bits em uma palavra e selecionar a parte que reverte os bits em cada byte, ficará com algo que apenas reverte 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 limpar as operações supérfluas de mascaramento de bits (eu as deixei para destacar o padrão), mas, se não, você pode reescrever a primeira linha desta maneira:

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

Normalmente, isso deve simplificar até uma única instrução de rotação na maioria das arquiteturas (ignorando que toda a operação é provavelmente uma instrução).

Em um processador RISC, as constantes grandes e complicadas podem causar dificuldades no compilador. Você pode calcular trivialmente cada uma das constantes da anterior, no entanto. Igual a:

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 quiser, você pode escrever isso como um loop. Não será eficiente, mas apenas por diversão:

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 simplificada de 32 bits do primeiro formulário:

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

Só pensei em adicionar minha própria solução aqui, pois ainda não a vi em lugar nenhum. É uma função modelada em C ++ pequena e portátil e portátil que usa apenas operações de bits.

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;
}
João
fonte
2

Estou realmente surpreso que ninguém tenha mencionado as funções htobeXX e betohXX. Eles são definidos em endian.he são muito semelhantes às funções de rede htonXX.

usuario
fonte
2

Usando os códigos abaixo, você pode alternar 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))
pz64_
fonte
1

Recentemente, escrevi 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 reverte os bytes no argumento passado. Exemplos de 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 imprime:

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

O acima é perfeitamente capaz de copiar / colar, mas há muita coisa acontecendo aqui, então vou detalhar como funciona peça por peça:

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

A seguir, o uso de uma variável nomeada REVERSE_BYTEScomo forcontador do loop. O nome da macro em si é usado como um nome de variável para garantir que não colidir com outros símbolos que possam estar no escopo onde quer que a macro seja usada. Como o nome está sendo usado na expansão da macro, ele não será expandido novamente quando usado como um nome de variável aqui.

Dentro do for loop, há dois bytes sendo referenciados e trocados por XOR (portanto, um nome temporário de variável não é necessário):

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

__VA_ARGS__representa o que foi dado à macro e é usado para aumentar a flexibilidade do que pode ser passado (embora não muito). O endereço desse argumento é levado e convertido em um unsigned charponteiro para permitir a troca de seus bytes via array[] assinatura de .

O ponto peculiar final é a falta de {} aparelho. Eles não são necessários porque todas as etapas de cada troca são unidas ao operador de vírgula , tornando-as uma declaração.

Por fim, vale ressaltar que essa não é a abordagem ideal se a velocidade for uma prioridade. Se esse é um fator importante, algumas das macros específicas de tipo ou diretivas específicas de plataforma mencionadas em outras respostas provavelmente são uma opção melhor. Essa abordagem, no entanto, é portátil para todos os tipos, todas as principais plataformas e as linguagens C e C ++.

Ryan Hilbert
fonte
encontrou isso em algum lugar em algum código. confundiu o inferno fora de mim. Obrigada pelo esclarecimento. No entanto, por que o uso de __VA_ARGS__?
Asr9
0

Uau, eu não podia acreditar em algumas das respostas que li aqui. Na verdade, há uma instrução em assembly que faz isso mais rapidamente 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 que os intrínsecos sugeridos. Eu desmontei-os e olhei. A função acima não possui prólogo / epílogo, portanto praticamente não possui custos indiretos.

unsigned long _byteswap_ulong(unsigned long value);

Fazer 16 bits é igualmente fácil, com a exceção de que você usaria xchg al, ah. O bswap funciona apenas em registradores de 32 bits.

64 bits é um pouco mais complicado, mas não excessivamente. Muito melhor do que todos os exemplos acima com loops e modelos etc.

Existem algumas advertências aqui ... Em primeiro lugar, o bswap está disponível apenas nas CPUs 80x486 e acima. Alguém está planejando executá-lo em um 386?!? Nesse caso, você ainda pode substituir o bswap por ...

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

Também a montagem embutida está disponível apenas no código x86 no Visual Studio. Uma função simples não pode ser alinhada e também não está disponível nas versões x64. Nesse exemplo, você precisará usar as intrínsecas do compilador.

O Soldador
fonte
1
_byteswap_ulonge _uint64(por exemplo, na resposta aceita), ambos compilam para usar a bswapinstrução. Eu ficaria surpreso, mas interessado em saber se esse asm é muito mais rápido, pois apenas omite o prólogo / epílogo - você o avaliou?
ZachB 28/10
@stdcall A pergunta não pediu uma solução portátil nem mencionou nada sobre uma plataforma. Como minha resposta disse, o exposto acima é sobre o caminho mais rápido para a troca endian. Claro, se você está escrevendo isso em uma plataforma não-X86, isso não vai funcionar, mas como eu também mencionei, você estará limitado às intrínsecas do compilador, se o seu compilador os suportar.
O soldador
@ZachB Nesse caso em particular, acho que a omissão do prólogo e do epílogo proporcionará uma economia decente, porque você está basicamente executando apenas uma instrução. O prólogo terá que empurrar para a pilha, fazer uma subtração, definir o ponteiro base e, em seguida, semelhante no final. Eu não o comparei, mas o acima tem uma cadeia de dependência 0 que você simplesmente não vai conseguir sem que fique nua. Talvez um bom compilador o incorporasse, mas você está em um estádio diferente.
O soldador
2
Possivelmente. Mas observe que, no caso comum de trocar uma matriz de números, as intrínsecas do compilador discutidas em outras respostas usarão extensões SSE / AVX e emitirão PSHUFB, que supera o BSWAP. Veja wm.ite.pl/articles/reverse-array-of-bytes.html
ZachB
É uma má forma do IMHO postar uma solução específica da plataforma, quando o OP não especificou que eles precisavam apenas de uma solução para x86. E para depreciar as outras soluções, quando o seu está inutilizável em muitos amplamente utilizados, tais como iOS e Android OS (que usam ARM ou MIPS CPUs.)
Jens Alfke
0

Técnica portátil para implementar acessadores endian não alinhados e não alinhados, otimizados para otimizadores. Eles trabalham em todos os compiladores, alinhamentos de limites e pedidos de bytes. Essas rotinas não alinhadas são suplementadas ou discutidas, dependendo do endian nativo e do alinhamento. Lista parcial, mas você entendeu. BO * são valores constantes com base na ordem 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];
}

Esses typedefs têm o benefício de gerar erros de compilador se não forem usados ​​com acessadores, reduzindo assim os erros esquecidos do acessador.

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 */
BSalita
fonte
2
Para esta pergunta, a marca C ++ faz a diferença. Há muito comportamento indefinido devido ao C ++ e à união.
JWW
0

Veja como ler um duplo armazenado no formato IEEE 754 de 64 bits, mesmo que o computador host use 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 restante do conjunto de funções, incluindo as rotinas de gravação e de número inteiro, veja meu projeto no github

https://github.com/MalcolmMcLean/ieee754

Malcolm McLean
fonte
0

A troca de bytes com o velho truque de 3 etapas xor em torno de um pivô em uma função de modelo fornece uma solução O (ln2) rápida e flexível que não requer uma biblioteca, o estilo aqui também rejeita os tipos de 1 byte:

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);
    }
}
Quinn Carver
fonte
0

Parece que o caminho seguro 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 exemplo acima seria um não operacional se você estivesse em um sistema big endian, portanto, procuraria o que sua plataforma usa como uma condição de tempo de compilação para decidir se o htons é um não operacional. Afinal, é O (n). 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
cycollins
fonte
0

Se você possui C ++ 17, adicione este cabeçalho

#include <algorithm>

Use esta função de modelo para trocar os bytes:

template <typename T>
void swapEndian(T& buffer)
{
    static_assert(std::is_pod<T>::value, "swapEndian support POD type only");
    char* startIndex = static_cast<char*>((void*)buffer.data());
    char* endIndex = startIndex + sizeof(buffer);
    std::reverse(startIndex, endIndex);
}

chame assim:

swapEndian (stlContainer);
Thinkal VB
fonte
-4

Procure mudar um pouco, pois isso é basicamente tudo o que você precisa fazer para trocar de little -> big endian. Então, dependendo do tamanho do bit, você altera a maneira como faz a troca de bits.

Barão Vermelho
fonte