Detectando Endianness programaticamente em um programa C ++

211

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

Jay T
fonte
4
Por uma questão de exaustividade, aqui está um link para a pergunta de outra pessoa sobre a tentativa de ordenação de calibre (em tempo de compilação): stackoverflow.com/questions/280162/...
Faisal Vali
14
Por que não determinar endianness em tempo de compilação? Não pode mudar em tempo de execução.
ephemient
3
AFAIK, não há uma maneira confiável e universal de fazer isso. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

Respostas:

174

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!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

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 .

David Cournapeau
fonte
51
Eu não recomendo nomear uma variável "bint" :)
mkb
42
você tem certeza de que isso está bem definido? Em C ++ apenas um membro da união pode ser ativo em um tempo - ou seja, você não pode atribuir usando um membro do nome e ler usando outro (embora haja uma exceção para estruturas compatíveis de layout)
Faisal Vali
27
@ Matt: Eu olhei no Google, e bint parece ter um significado em Inglês que eu não estava ciente de :)
David Cournapeau
17
Eu testei isso e no gcc 4.0.1 e no gcc 4.4.1 o resultado dessa função pode ser determinado no tempo de compilação e tratado como uma constante. Isso significa que o compilador será descartado se ramificações que dependem exclusivamente do resultado dessa função e nunca forem colocadas na plataforma em questão. Provavelmente isso não é verdade para muitas implementações do htonl.
omniforme
6
Esta solução é realmente portátil? E se CHAR_BIT != 8?
Zorgit 30/05
80

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

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

A manipulação de bits pode ser mais rápida, mas dessa maneira é simples, direta e praticamente impossível de bagunçar.

Eric Petroelje
fonte
1
As operações de conversão de rede também podem ser usadas para converter tudo em big endian, resolvendo outros problemas que Jay pode estar encontrando.
Brian
6
@sharptooth - slow é um termo relativo, mas sim, se a velocidade é realmente um problema, use-a uma vez no início do programa e defina uma variável global com o endianness.
Eric Petroelje
5
O htonl tem outro problema: em algumas plataformas (windows?), ele não reside na biblioteca de tempo de execução C adequada, mas em bibliotecas adicionais relacionadas à rede (soquete, etc ...). Isso é um obstáculo para apenas uma função, caso você não precise da biblioteca.
David Cournapeau 7/07/2009
7
Observe que no Linux (gcc), o htonl está sujeito a dobragem constante no tempo de compilação; portanto, uma expressão desse formulário não possui sobrecarga de tempo de execução (ou seja, é dobrada constantemente para 1 ou 0 e a eliminação do código morto remove o outro ramo do if)
bdonlan
2
Além disso, no x86, o htonl pode ser (e é, no Linux / gcc) implementado de forma muito eficiente usando o assembler em linha, principalmente se você direcionar uma microarquitetura com suporte para a BSWAPoperação.
bdonlan
61

Por favor, veja este artigo :

Aqui está um código para determinar qual é o tipo da sua máquina

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}
Andrew Hare
fonte
25
Lembre-se de que depende de int e char de diferentes comprimentos, o que quase sempre ocorre, mas não é garantido.
21730 David Thornley
10
Eu trabalhei em sistemas embarcados onde short int e char tinham o mesmo tamanho ... Não me lembro se o int regular também tinha esse tamanho (2 bytes) ou não.
Rmeador 16/06/09
2
por que esta resposta é praticamente a única resposta que não está me fazendo pensar "cara, o que você está fazendo?", que é o caso da maioria das respostas aqui: o
hanshenrik
2
O @Shillard int deve ser pelo menos tão grande, mas não há nenhum requisito no padrão para que o char seja restrito a menos! Se você tem um olhar para a família TI F280x, você vai descobrir que CHAR_BIT tem 16 anos e sizeof (int) == sizeof (char), enquanto os limites que você menciona são mantidos absolutamente bem ...
Aconcagua
5
Por que não usar uint8_t e uint16_t?
Rodrigo
58

Você pode usar std::endianse tiver acesso ao compilador C ++ 20, como GCC 8+ ou Clang 7+.

Nota: std::endiancomeç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>.

#include <bit>

if constexpr (std::endian::native == std::endian::big)
{
    // Big endian system
}
else if constexpr (std::endian::native == std::endian::little)
{
    // Little endian system
}
else
{
    // Something else
}
Lyberta
fonte
5
Como todo mundo que tenho acesso aos rascunhos / propostas de C ++ 17 e 20, mas, a partir de agora, algum compilador de C ++ 20 já existe?
Xeverous 29/10
@ Xeverous Requer apenas enumerações com escopo definido, então suspeito que a maioria dos fornecedores o adicione à sua implementação stdlib como uma de suas alterações anteriores.
Pharap
O @Xeverous GCC 8 foi lançado e o suporta.
Lyberta
Das mais de 30 respostas à pergunta, essa parece ser a única, completamente precisa (com outra resposta que é pelo menos correta em parte).
IInspectable em 01/01
40

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"

conta
fonte
8
Não acredito que isso não tenha sido votado mais alto. Não é como se o endianness fosse alterado em um programa compilado, portanto nunca há necessidade de um teste de tempo de execução.
Dolda2000
@ Dolda2000 Poderia ver os modos endian do ARM.
Tyzoid
10
@ Tyzoid: Não, um programa compilado sempre será executado no modo endian para o qual foi compilado, mesmo que o processador seja capaz.
Dolda2000
16

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

:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1

Em uma máquina PPC, recebo:

:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1

(A :| gcc -dM -E -x c -mágica imprime todas as macros incorporadas).

DaveR
fonte
7
Essas macros não aparecem consistentemente. Por exemplo, no gcc 4.4.5 do repositório Redhat 6, a execução echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'não retorna nada, enquanto o gcc 3.4.3 (de /usr/sfw/binqualquer 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).
Brian Vandenberg
15

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

int32 DNA_GetEndianness(void)
{
    union 
    {
        uint8  c[4];
        uint32 i;
    } u;

    u.i = 0x01020304;

    if (0x04 == u.c[0])
        return DNA_ENDIAN_LITTLE;
    else if (0x01 == u.c[0])
        return DNA_ENDIAN_BIG;
    else
        return DNA_ENDIAN_UNKNOWN;
}

Desmontagem

PUBLIC  _DNA_GetEndianness
; Function compile flags: /Ogtpy
; File c:\development\dna\source\libraries\dna\endian.c
;   COMDAT _DNA_GetEndianness
_TEXT   SEGMENT
_DNA_GetEndianness PROC                 ; COMDAT

; 11   :     union 
; 12   :     {
; 13   :         uint8  c[4];
; 14   :         uint32 i;
; 15   :     } u;
; 16   : 
; 17   :     u.i = 1;
; 18   : 
; 19   :     if (1 == u.c[0])
; 20   :         return DNA_ENDIAN_LITTLE;

    mov eax, 1

; 21   :     else if (1 == u.c[3])
; 22   :         return DNA_ENDIAN_BIG;
; 23   :     else
; 24   :        return DNA_ENDIAN_UNKNOWN;
; 25   : }

    ret
_DNA_GetEndianness ENDP
END

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

Coentro
fonte
11
Você não precisa recompilar para executar em uma plataforma diferente?
bobobobo
2
Embora funcione bem para o MSVC, não funciona em todas as versões do GCC em todas as circunstâncias. Portanto, uma "verificação em tempo de execução" dentro de um loop crítico pode ser desassociada corretamente em tempo de compilação, ou não. Não há 100% de garantia.
Ciano
21
Não existe um processador x86 big-endian. Mesmo se você executar o Ubuntu em um processador compatível (como ARM ou MIPS), os executáveis ​​do ELF serão sempre grandes (MSB) ou pequenos (LSB) endian. Nenhum executável diabólico pode ser criado, portanto, nenhuma verificação de tempo de execução é necessária.
Fabel
4
Para desligar a otimização neste uso do método 'união volátil ...' Ele diz compilador que 'u' pode ser mudado em outro lugar e os dados devem ser carregados
mishmashru
1
Para que essa função retorne um valor diferente em tempo de execução do que o otimizador está calculando, isso implica que o otimizador está com erros. Você está dizendo que existem exemplos de código binário otimizado compilado que podem ser executados de maneira portável em duas arquiteturas diferentes de endianness diferente, apesar das suposições óbvias feitas pelo otimizador (durante todo o programa) durante a compilação que parecem ser incompatíveis com pelo menos um desses arquiteturas?
Scott
13

Declare uma variável int:

int variable = 0xFF;

Agora use ponteiros char * para várias partes e verifique o que há nessas partes.

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

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.

dente afiado
fonte
8

Para obter mais detalhes, consulte este artigo do projeto de código Conceitos básicos sobre Endianness :

Como testar dinamicamente o tipo Endian em tempo de execução?

Conforme explicado nas Perguntas frequentes sobre animação por computador, você pode usar a seguinte função para verificar se seu código está sendo executado em um sistema Little ou Big Endian: Recolher

#define BIG_ENDIAN      0
#define LITTLE_ENDIAN   1
int TestByteOrder()
{
   short int word = 0x0001;
   char *byte = (char *) &word;
   return(byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN);
}

Este código atribui o valor 0001h a um número inteiro de 16 bits. Um ponteiro de caractere é então designado para apontar para o primeiro byte (menos significativo) do valor inteiro. Se o primeiro byte do número inteiro for 0x01h, o sistema será Little-Endian (o 0x01h está no endereço mais baixo ou menos significativo). Se for 0x00h, o sistema é Big-Endian.

Nenhum
fonte
6

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.

fuzzyTew
fonte
1
Para sua informação, o link "quatro tipos diferentes de endianness" está quebrado,
Remy Lebeau
fixado e wiki
fuzzyTew
5

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:

short temp = 0x1234;
char* tempChar = (char*)&temp;

Você obterá que tempChar seja 0x12 ou 0x34, a partir do qual você conhecerá o endianness.

samoz
fonte
3
Isso depende de curto ser exatamente 2 bytes, o que não é garantido.
Sharptooth 16/06/09
3
Seria uma aposta bastante segura, embora baseada nas duas arquiteturas dadas na pergunta.
Daemin 16/06/09
8
Inclua stdint.he use int16_tpara provas futuras contra curto ser diferente em outra plataforma.
Denise Skidmore #
4

Eu faria algo assim:

bool isBigEndian() {
    static unsigned long x(1);
    static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
    return result;
}

Nessa linha, você obteria uma função com economia de tempo que faz o cálculo apenas uma vez.

Jeremy Mayhew
fonte
você pode incorporar isso? não tenho certeza se em linha causa vários blocos de memória das variáveis estáticos
aah134
4

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.

#include <stdint.h>

#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0

template <typename T>
T endian(T w, uint32_t endian)
{
    // this gets optimized out into if (endian == host_endian) return w;
    union { uint64_t quad; uint32_t islittle; } t;
    t.quad = 1;
    if (t.islittle ^ endian) return w;
    T r = 0;

    // decent compilers will unroll this (gcc)
    // or even convert straight into single bswap (clang)
    for (int i = 0; i < sizeof(r); i++) {
        r <<= 8;
        r |= w & 0xff;
        w >>= 8;
    }
    return r;
};

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.

kat
fonte
4
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}
Paolo Brandoli
fonte
1
Isso seria equivalente? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Emanuel
4

Não use a union!

C ++ não permite punção de tipo via unions!
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::endiano <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::endiana 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

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

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 unions, isso é expressamente permitido pelo sistema de tipos do C ++.

Também é importante lembrar que a portabilidade ideal static_castdeve ser usada,
porque a reinterpret_castimplementação está definida.

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: ... a charou unsigned chartype.

C ++ 11 em diante

enum class endianness
{
    little = 0,
    big = 1,
};

inline endianness get_system_endianness()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
}

C ++ 11 em diante (sem enumeração)

inline bool is_system_little_endian()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

C ++ 98 / C ++ 03

inline bool is_system_little_endian()
{
    const int value = 0x01;
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}
Pharap
fonte
3
union {
    int i;
    char c[sizeof(int)];
} x;
x.i = 1;
if(x.c[0] == 1)
    printf("little-endian\n");
else    printf("big-endian\n");

Essa é outra solução. Semelhante à solução de Andrew Hare.

Neeraj
fonte
3

não testado, mas na minha opinião, isso deve funcionar? porque será 0x01 no little endian e 0x00 no big endian?

bool runtimeIsLittleEndian(void)
{
 volatile uint16_t i=1;
 return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
hanshenrik
fonte
3

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:

union {
  uint16_t s;
  unsigned char c[2];
} constexpr static  d {1};

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}
zhaorufei
fonte
2
Existe um motivo específico para você usar char não assinado em uint8_t?
Kevin
0 sobrecarga de tempo de execução ... eu gosto!
hanshenrik 11/09/2015
Eu acho que isso detecta endiannes da máquina de construção, não o alvo?
hutorny 23/10/2015
2
Este UB não está em C ++?
precisa
6
isso não é legal no contexto constexpr. Você não pode acessar um membro de um sindicato que não foi inicializado diretamente. Não há como legalmente detectar endianness em tempo de compilação sem a mágica do pré-processador.
Richard Hodges
2

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
1

A menos que o cabeçalho endian seja somente GCC, ele fornece macros que você pode usar.

#include "endian.h"
...
if (__BYTE_ORDER == __LITTLE_ENDIAN) { ... }
else if (__BYTE_ORDER == __BIG_ENDIAN) { ... }
else { throw std::runtime_error("Sorry, this version does not support PDP Endian!");
...
Mark A. Libby
fonte
Não são estes __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__e __ORDER_BIG_ENDIAN__?
Xeverous
1

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:

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

O mesmo código, tentando levar em consideração a resistência da máquina:

i = *((int*)data);
#ifdef BIG_ENDIAN
/* swap the bytes */
i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) | (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
#endif
fjardon
fonte
Que ótima ideia! E agora vamos transferir seus números inteiros via soquete de rede para um dispositivo desconhecido.
Maksym Ganenko 13/11/19
@MaksymGanenko Não recebo seu comentário. Isso é ironia? Estou não sugerindo não especificar endianness de dados serializados. Estou sugerindo não escrever código dependente do endianness da máquina que recebe os dados.
fjardon
@MaksymGanenko Se você recusou, pode explicar por que a resposta está errada. No mínimo, para ajudar os leitores em potencial a entender por que eles não devem seguir minha resposta.
Fjardon 17/11/19
0
int i=1;
char *c=(char*)&i;
bool littleendian=c;
Jon Bright
fonte
0

Que tal agora?

#include <cstdio>

int main()
{
    unsigned int n = 1;
    char *p = 0;

    p = (char*)&n;
    if (*p == 1)
        std::printf("Little Endian\n");
    else 
        if (*(p + sizeof(int) - 1) == 1)
            std::printf("Big Endian\n");
        else
            std::printf("What the crap?\n");
    return 0;
}
Abhay
fonte
0

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 .

#include <limits.h>

#if UCHAR_MAX == UINT_MAX
#error endianness irrelevant as sizeof(int) == 1
#endif

#define wicked_cast(TYPE, VALUE) \
    (((union { __typeof__(VALUE) src; TYPE dest; }){ .src = VALUE }).dest)

_Bool is_little_endian(void)
{
    return wicked_cast(unsigned char, 1u);
}

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.

Christoph
fonte
0

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.

Fabel
fonte
-1

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:

#include <stdint.h>

int* _BE = 0;

int is_big_endian() {
    if (_BE == 0) {
        uint16_t* teste = (uint16_t*)malloc(4);
        *teste = (*teste & 0x01FE) | 0x0100;
        uint8_t teste2 = ((uint8_t*) teste)[0];
        free(teste);
        _BE = (int*)malloc(sizeof(int));
        *_BE = (0x01 == teste2);
    }
    return *_BE;
}

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.

Tex Killer
fonte
Por que alocar 4 bytes para trabalhar com um valor de 2 bytes? Por que mascarar um valor indeterminado 0x7FE? Por que usar malloc()? isso é um desperdício. E _BEcomo 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.
Remy Lebeau
@RemyLebeau, o ponto principal da minha resposta foi produzir um código que não é otimizado pelo compilador. Claro, seu código é muito mais simples, mas com as otimizações ativadas, ele se tornará um booleano constante após a compilação. Como afirmei na minha resposta, na verdade não sei se existe alguma maneira de compilar o código C de maneira que o mesmo executável seja executado nas duas ordens de bytes, e também estava curioso para saber se poderia fazer a verificação no tempo de execução apesar das otimizações ativadas.
Tex Assassino
@TexKiller, por que não simplesmente desativar as otimizações para o código? Usando volatile, ou #pragmaetc.
Remy Lebeau
@RemyLebeau, eu não conhecia essas palavras-chave na época e apenas tomei como um pequeno desafio impedir a otimização do compilador com o que eu sabia.
Tex Killer
-1

embora não exista uma maneira rápida e padrão de determiná-lo, isso resultará em:

#include <stdio.h> 
int main()  
{ 
   unsigned int i = 1; 
   char *c = (char*)&i; 
   if (*c)     
       printf("Little endian"); 
   else
       printf("Big endian"); 
   getchar(); 
   return 0; 
} 
yekanchi
fonte
-1

Veja Ilustração Endianness - Código de Nível C.

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANNESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANNESS CheckArchEndianalityV1( void )
{
    int Endian = 0x00000001; // assuming target architecture is 32-bit    

    // as Endian = 0x00000001 so MSB (Most Significant Byte) = 0x00 and LSB (Least     Significant Byte) = 0x01
    // casting down to a single byte value LSB discarding higher bytes    

    return (*(char *) &Endian == 0x01) ? LITTLEENDIAN : BIGENDIAN;
} 
gimel
fonte
-2

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:

#include <stdio.h>

int main(void){
    int i=1;
    unsigned char* ii = &i;

    printf("This computer is %s endian.\n", ((ii[0]==1) ? "little" : "big"));
    return 0;
}

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.

Arquimedes520
fonte
isso seria melhorado usando int32t.
shuttle87
1
^ se você quiser nitpick, o melhor aqui é int16_fast_t. e @ código atual do Archimedes520 não vai funcionar em um arco, onde int é int8 nativamente;) (que pode ir contra as normas c, em primeiro lugar, embora)
hanshenrik