Existe uma maneira programática de detectar se você está ou não em uma arquitetura big-endian ou little-endian? Eu preciso ser capaz de escrever código que será executado em um sistema Intel ou PPC e usar exatamente o mesmo código (ou seja, sem compilação condicional).
c++
algorithm
endianness
Jay T
fonte
fonte
Respostas:
Não gosto do método com base no tipo de punção - ele geralmente é avisado pelo compilador. É exatamente para isso que servem os sindicatos!
O princípio é equivalente ao tipo de caso, conforme sugerido por outros, mas isso é mais claro - e de acordo com C99, é garantido que ele esteja correto. O gcc prefere isso em comparação com a conversão direta do ponteiro.
Isso também é muito melhor do que corrigir o endianness em tempo de compilação - para sistemas operacionais que suportam multi-arquitetura (binário gordo no Mac OS x, por exemplo), isso funcionará para o ppc / i386, embora seja muito fácil bagunçar as coisas .
fonte
CHAR_BIT != 8
?Você pode fazer isso definindo um int e mascarando os bits, mas provavelmente a maneira mais fácil é usar as operações de conversão de bytes de rede integradas (uma vez que a ordem de bytes de rede é sempre grande endian).
A manipulação de bits pode ser mais rápida, mas dessa maneira é simples, direta e praticamente impossível de bagunçar.
fonte
BSWAP
operação.Por favor, veja este artigo :
fonte
Você pode usar
std::endian
se tiver acesso ao compilador C ++ 20, como GCC 8+ ou Clang 7+.Nota:
std::endian
começou em,<type_traits>
mas foi transferido para<bit>
a reunião de 2019 em Colônia. GCC 8, Clang 7, 8 e 9 estão dentro,<type_traits>
enquanto GCC 9+ e Clang 10+ estão dentro<bit>
.fonte
Isso normalmente é feito em tempo de compilação (especialmente por motivos de desempenho) usando os arquivos de cabeçalho disponíveis no compilador ou crie seus próprios. No Linux, você tem o arquivo de cabeçalho "/usr/include/endian.h"
fonte
Surpreendi que ninguém tenha mencionado as macros que o pré-processador define por padrão. Embora isso varie dependendo da sua plataforma; eles são muito mais limpos do que ter que escrever seu próprio cheque endian.
Por exemplo; se observarmos as macros internas definidas pelo GCC (em uma máquina X86-64):
Em uma máquina PPC, recebo:
(A
:| gcc -dM -E -x c -
mágica imprime todas as macros incorporadas).fonte
echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'
não retorna nada, enquanto o gcc 3.4.3 (de/usr/sfw/bin
qualquer maneira) no Solaris tem uma definição nessas linhas. Já vi problemas semelhantes no VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4).Ehm ... Surpreende-me que ninguém tenha percebido que o compilador simplesmente otimizará o teste e colocará um resultado fixo como valor de retorno. Isso torna todos os exemplos de código acima, efetivamente inúteis. A única coisa que seria retornada é a persistência no tempo de compilação! E sim, testei todos os exemplos acima. Aqui está um exemplo com o MSVC 9.0 (Visual Studio 2008).
Código C puro
Desmontagem
Talvez seja possível desativar QUALQUER otimização em tempo de compilação para essa função, mas não sei. Caso contrário, talvez seja possível codificá-lo na montagem, embora isso não seja portátil. E mesmo assim, isso pode ser otimizado. Isso me faz pensar que preciso de um montador realmente ruim, implementar o mesmo código para todas as CPUs / conjuntos de instruções existentes e bem ... não importa.
Além disso, alguém aqui disse que endianness não muda durante o tempo de execução. ERRADO. Existem máquinas bi-endian por aí. Sua endianness pode variar durante a execução. Além disso, não há apenas Little Endian e Big Endian, mas também outras endianidades (que palavra).
Eu odeio e amo codificar ao mesmo tempo ...
fonte
Declare uma variável int:
Agora use ponteiros char * para várias partes e verifique o que há nessas partes.
Dependendo de qual deles aponta para o byte 0xFF, agora é possível detectar endianness. Isso requer sizeof (int)> sizeof (char), mas é definitivamente verdade para as plataformas discutidas.
fonte
Para obter mais detalhes, consulte este artigo do projeto de código Conceitos básicos sobre Endianness :
fonte
A maneira C ++ tem sido usar o boost , onde verificações e transmissões do pré-processador são compartimentadas em bibliotecas muito testadas.
A Biblioteca Predef (boost / predef.h) reconhece quatro tipos diferentes de endianness .
A Biblioteca Endian foi planejada para ser submetida ao padrão C ++ e suporta uma ampla variedade de operações em dados sensíveis a endian.
Conforme declarado nas respostas acima, o Endianness fará parte do c ++ 20.
fonte
A menos que você esteja usando uma estrutura que foi portada para os processadores PPC e Intel, você terá que fazer compilações condicionais, uma vez que as plataformas PPC e Intel têm arquiteturas de hardware, pipelines, barramentos, etc. completamente diferentes. Isso torna o código de montagem completamente diferente entre os dois.
Quanto à localização de endianness, faça o seguinte:
Você obterá que tempChar seja 0x12 ou 0x34, a partir do qual você conhecerá o endianness.
fonte
stdint.h
e useint16_t
para provas futuras contra curto ser diferente em outra plataforma.Eu faria algo assim:
Nessa linha, você obteria uma função com economia de tempo que faz o cálculo apenas uma vez.
fonte
Como mencionado acima, use truques de união.
Porém, existem alguns problemas com os recomendados acima, principalmente o acesso desalinhado à memória é notoriamente lento para a maioria das arquiteturas, e alguns compiladores nem sequer reconhecem tais predicados constantes, a menos que estejam alinhados com as palavras.
Como o mero teste endian é chato, aqui vai a função (modelo) que inverterá a entrada / saída do número inteiro arbitrário de acordo com a sua especificação, independentemente da arquitetura do host.
Uso:
Para converter de endian especificado em host, use:
host = endian(source, endian_of_source)
Para converter de host endian para determinado endian, use:
output = endian(hostsource, endian_you_want_to_output)
O código resultante é tão rápido quanto escrever montagem manual no clang, no gcc é um pouco mais lento (desenrolado &, <<, >>, | para cada byte), mas ainda decente.
fonte
fonte
#define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Não use a
union
!C ++ não permite punção de tipo via
union
s!Ler de um campo de união que não foi o último campo gravado é um comportamento indefinido !
Muitos compiladores suportam isso como extensões, mas o idioma não garante.
Veja esta resposta para mais detalhes:
https://stackoverflow.com/a/11996970
Existem apenas duas respostas válidas que são garantidas como portáteis.
A primeira resposta, se você tiver acesso a um sistema que suporte C ++ 20,
será usar
std::endian
o<type_traits>
cabeçalho.(No momento em que este artigo foi escrito, o C ++ 20 ainda não foi lançado, mas, a menos que algo afete
std::endian
a inclusão, essa deve ser a maneira preferida de testar o endianness no tempo de compilação a partir do C ++ 20).C ++ 20 em diante
Antes do C ++ 20, a única resposta válida é armazenar um número inteiro e, em seguida, inspecionar seu primeiro byte por meio de punção de tipo.
Diferentemente do uso de
union
s, isso é expressamente permitido pelo sistema de tipos do C ++.Também é importante lembrar que a portabilidade ideal
static_cast
deve ser usada,porque a
reinterpret_cast
implementação está definida.C ++ 11 em diante
C ++ 11 em diante (sem enumeração)
C ++ 98 / C ++ 03
fonte
Essa é outra solução. Semelhante à solução de Andrew Hare.
fonte
não testado, mas na minha opinião, isso deve funcionar? porque será 0x01 no little endian e 0x00 no big endian?
fonte
Declarar:
Minha postagem inicial foi declarada incorretamente como "tempo de compilação". Não é, é até impossível no padrão C ++ atual. O constexpr NÃO significa que a função sempre faz computação em tempo de compilação. Obrigado Richard Hodges pela correção.
tempo de compilação, não macro, solução constexpr C ++ 11:
fonte
Você também pode fazer isso através do pré-processador usando algo como o arquivo de cabeçalho boost, que pode ser encontrado boost endian
fonte
A menos que o cabeçalho endian seja somente GCC, ele fornece macros que você pode usar.
fonte
__BYTE_ORDER__
,__ORDER_LITTLE_ENDIAN__
e__ORDER_BIG_ENDIAN__
?Se você não deseja compilação condicional, basta escrever código independente endian. Aqui está um exemplo (retirado de Rob Pike ):
Lendo um número inteiro armazenado no little-endian no disco, de uma maneira independente do endian:
O mesmo código, tentando levar em consideração a resistência da máquina:
fonte
fonte
Que tal agora?
fonte
Aqui está outra versão em C. Ele define uma macro chamada
wicked_cast()
punição de tipo em linha via literais de união C99 e o__typeof__
operador não padrão .Se números inteiros são valores de byte único, endianness não faz sentido e um erro em tempo de compilação será gerado.
fonte
A maneira como os compiladores C (pelo menos todos os que eu conheço) funcionam no endianness deve ser decidida no momento da compilação. Mesmo para processadores diabéticos (como o ARM e o MIPS), é necessário escolher a endianness no momento da compilação. Além disso, a endianness é definida em todos os formatos de arquivo comuns para executáveis (como ELF). Embora seja possível criar um blob binário de código biandiano (para alguma exploração do servidor ARM, talvez?), Provavelmente isso deve ser feito em assembly.
fonte
Como apontado pelo Coriiander, a maioria (senão todos) desses códigos aqui será otimizada no tempo de compilação, para que os binários gerados não verifiquem a "continuidade" no tempo de execução.
Foi observado que um determinado executável não deve ser executado em duas ordens de bytes diferentes, mas não tenho idéia se esse é sempre o caso, e parece um hack para mim verificar no momento da compilação. Então eu codifiquei esta função:
O MinGW não conseguiu otimizar esse código, apesar de otimizar os outros códigos daqui. Eu acredito que é porque deixo o valor "aleatório" que foi alocado na memória de bytes menores como era (pelo menos 7 de seus bits), para que o compilador não possa saber qual é esse valor aleatório e não otimiza a função ausente.
Também codifiquei a função para que a verificação seja realizada apenas uma vez e o valor de retorno seja armazenado para os próximos testes.
fonte
0x7FE
? Por que usarmalloc()
? isso é um desperdício. E_BE
como um vazamento de memória (embora pequeno) e uma condição de corrida aguardando para acontecer, os benefícios de armazenar em cache o resultado dinamicamente não valem a pena. Eu faria algo mais parecido com isto:static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&teste)[1]); }
simples e eficaz e muito menos trabalho para executar em tempo de execução.volatile
, ou#pragma
etc.embora não exista uma maneira rápida e padrão de determiná-lo, isso resultará em:
fonte
Veja Ilustração Endianness - Código de Nível C.
fonte
Eu estava examinando o livro: Sistema de Computadores: a perspectiva de um programador , e há um problema para determinar qual endian é esse no programa C.
Eu usei o recurso do ponteiro para fazer isso da seguinte maneira:
Como o int ocupa 4 bytes, e char ocupa apenas 1 bytes. Poderíamos usar um ponteiro char para apontar para int com o valor 1. Portanto, se o computador é pouco endian, o char para o qual o apontador char aponta é com valor 1, caso contrário, seu valor deve ser 0.
fonte