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.
c++
endianness
Uhall
fonte
fonte
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.Respostas:
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:
Para números de 32 bits:
Para números de 64 bits:
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 :
(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.
fonte
__builtin_bswapX
só está disponível a partir de 4,3-GCC em diantehtonl
,htons
etc. Você tem que saber a partir do contexto de sua situação quando realmente trocar os bytes.htonl
entohl
sem 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 emhtonl
entohl
.Simplificando:
uso:
swap_endian<uint32_t>(42)
.fonte
De falácia da ordem de bytes de Rob Pike:
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
data
fosse uma matriz deunsigned char
ouuint8_t
. Usarsigned char
ouchar
(se assinado) resultará nadata[x]
promoção para um número inteiro e,data[x] << 24
potencialmente, na mudança de 1 para o bit de sinal que é UB.fonte
Se você estiver fazendo isso para fins de compatibilidade de rede / host, deverá usar:
Se você estiver fazendo isso por algum outro motivo, uma das soluções byte_swap apresentadas aqui funcionaria perfeitamente.
fonte
htonl
entohl
não pode ir para little endian em uma plataforma big-endian.Peguei algumas sugestões deste post e as reuni para formar isso:
fonte
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:
fonte
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.fonte
Fizemos isso com modelos. Você poderia fazer algo assim:
fonte
Se você estiver fazendo isso para transferir dados entre plataformas diferentes, observe as funções ntoh e hton.
fonte
Da mesma maneira que você faz em C:
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.
fonte
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:
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.
fonte
htonl
e 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.)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:
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}.
fonte
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 ++:Resultado:
fonte
Eu gosto deste, apenas pelo estilo :-)
fonte
char[]
dizer 'Erro: tipo incompleto não é permitido'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 ????
É 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 auint8_t
. Portanto, a coisa certa a fazer é copiar isso para uma matriz de caracteres, trocá-lo e copiá-lo novamente (então nãoreinterpret_cast
). Observe que os compiladores são inteligentes o suficiente para converter o que você fez emreinterpret_cast
se eles são capazes de acessar bytes individuais, independentemente do alinhamento.Para usar esta função :
e agora
x
é diferente em endianness.fonte
new
/delete
para alocar um buffer para isso?!?sizeof(var)
é uma constante em tempo de compilação, então você pode fazerchar varSwapped[sizeof(var)]
. Ou você pode fazerchar *p = reinterpret_cast<char*>(&var)
e trocar no local.for(size_t i = 0 ; i < sizeof(var) ; i++)
vez de astatic_cast<long>
. (Ou, na verdade, a troca no local usará uma ascendente e uma descendentechar*
para que desapareça de qualquer maneira).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:
fonte
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 ->
fonte
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.
Isenção de responsabilidade: ainda não tentei compilar ou testá-lo.
fonte
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:
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:
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:
Se quiser, você pode escrever isso como um loop. Não será eficiente, mas apenas por diversão:
E, para completar, aqui está a versão simplificada de 32 bits do primeiro formulário:
fonte
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.
fonte
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.
fonte
Usando os códigos abaixo, você pode alternar entre BigEndian e LittleEndian facilmente
fonte
Recentemente, escrevi uma macro para fazer isso em C, mas é igualmente válida em C ++:
Ele aceita qualquer tipo e reverte os bytes no argumento passado. Exemplos de usos:
Que imprime:
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_BYTES
comofor
contador 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):__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 umunsigned char
ponteiro 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 ++.
fonte
__VA_ARGS__
?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 ...
É 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.
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 ...
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.
fonte
_byteswap_ulong
e_uint64
(por exemplo, na resposta aceita), ambos compilam para usar abswap
instruçã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?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.
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.
fonte
Veja como ler um duplo armazenado no formato IEEE 754 de 64 bits, mesmo que o computador host use um sistema diferente.
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
fonte
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:
fonte
Parece que o caminho seguro seria usar htons em cada palavra. Então, se você tem ...
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 ...
fonte
Se você possui C ++ 17, adicione este cabeçalho
Use esta função de modelo para trocar os bytes:
chame assim:
fonte
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.
fonte