ti-enxame.com

C Definição de macro para determinar a máquina big endian ou little endian?

Existe uma definição de macro de uma linha para determinar o endianness da máquina. Estou usando o código a seguir, mas convertê-lo em macro seria muito longo.

unsigned char test_endian( void )
{
    int test_var = 1;
    unsigned char test_endian* = (unsigned char*)&test_var;

    return (test_endian[0] == NULL);
}
91
manav m-n

Código suportando ordens de byte arbitrárias, pronto para ser colocado em um arquivo chamado order32.h:

#ifndef ORDER32_H
#define ORDER32_H

#include <limits.h>
#include <stdint.h>

#if CHAR_BIT != 8
#error "unsupported char size"
#endif

enum
{
    O32_LITTLE_ENDIAN = 0x03020100ul,
    O32_BIG_ENDIAN = 0x00010203ul,
    O32_PDP_ENDIAN = 0x01000302ul,      /* DEC PDP-11 (aka ENDIAN_LITTLE_Word) */
    O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 (aka ENDIAN_BIG_Word) */
};

static const union { unsigned char bytes[4]; uint32_t value; } o32_Host_order =
    { { 0, 1, 2, 3 } };

#define O32_Host_ORDER (o32_Host_order.value)

#endif

Você verificaria sistemas little endian via

O32_Host_ORDER == O32_LITTLE_ENDIAN
90
Christoph

Se você tiver um compilador que suporte literais compostos C99:

#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})

ou:

#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)

Em geral, você deve tentar escrever um código que não dependa do endianness da plataforma Host.


Exemplo de implementação independente de host ntohl() de endianness__:

uint32_t ntohl(uint32_t n)
{
    unsigned char *np = (unsigned char *)&n;

    return ((uint32_t)np[0] << 24) |
        ((uint32_t)np[1] << 16) |
        ((uint32_t)np[2] << 8) |
        (uint32_t)np[3];
}
41
caf

Não existe um padrão, mas em muitos sistemas, incluindo <endian.h>, você terá algumas definições para procurar.

33
Ignacio Vazquez-Abrams

Para detectar o endianness em tempo de execução, você precisa poder se referir à memória. Se você mantiver o padrão C, declarar uma variável na memória requer uma instrução, mas retornar um valor requer uma expressão. Eu não sei como fazer isso em uma única macro - é por isso que o gcc tem extensões :-)

Se você está disposto a ter um arquivo .h, você pode definir

static uint32_t endianness = 0xdeadbeef; 
enum endianness { BIG, LITTLE };

#define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \
                   : *(const char *)&endianness == 0xde ? BIG \
                   : assert(0))

e então você pode usar a macro ENDIANNESS como quiser.

25
Norman Ramsey

Se você quiser confiar apenas no pré-processador, é necessário descobrir a lista de símbolos predefinidos. Aritmética de pré-processador não tem conceito de endereçamento.

GCC no Mac define __LITTLE_ENDIAN__ ou __BIG_ENDIAN__

$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1

Em seguida, você pode adicionar mais diretivas condicionais de pré-processador com base na detecção de plataforma como #ifdef _WIN32 etc.

19
Gregory Pakosz

Eu acredito que isso é o que foi pedido. Eu só testei isso em uma pequena máquina endian sob msvc. Alguém pode confirmar em uma máquina big endian. 

    #define LITTLE_ENDIAN 0x41424344UL 
    #define BIG_ENDIAN    0x44434241UL
    #define PDP_ENDIAN    0x42414443UL
    #define ENDIAN_ORDER  ('ABCD') 

    #if ENDIAN_ORDER==LITTLE_ENDIAN
        #error "machine is little endian"
    #Elif ENDIAN_ORDER==BIG_ENDIAN
        #error "machine is big endian"
    #Elif ENDIAN_ORDER==PDP_ENDIAN
        #error "jeez, machine is PDP!"
    #else
        #error "What kind of hardware is this?!"
    #endif

Como uma nota lateral (compilador específico), com um compilador agressivo você pode usar a otimização "eliminação de código morto" para obter o mesmo efeito que um tempo de compilação #if assim:

    unsigned yourOwnEndianSpecific_htonl(unsigned n)
    {
        static unsigned long signature= 0x01020304UL; 
        if (1 == (unsigned char&)signature) // big endian
            return n;
        if (2 == (unsigned char&)signature) // the PDP style
        {
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        if (4 == (unsigned char&)signature) // little endian
        {
            n = (n << 16) | (n >> 16);
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        // only weird machines get here
        return n; // ?
    }

O acima depende do fato de que o compilador reconhece os valores constantes em tempo de compilação, remove completamente o código dentro de if (false) { ... } e substitui o código como if (true) { foo(); } por foo(); O pior cenário: o compilador não faz a otimização, você ainda obtém o código correto, mas pouco mais devagar.

14
ggpp23

Se você está procurando um teste de tempo de compilação e está usando o gcc, você pode fazer:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

Veja a documentação do gcc para mais informações.

9
Jezz

Você pode na verdade acessa a memória de um objeto temporário usando um literal composto (C99):

#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})

Qual GCC avaliará em tempo de compilação.

7
u0b34a0f6ae

A 'biblioteca de rede C' oferece funções para lidar com o endian'ness. Ou seja, htons (), htonl (), ntohs () e ntohl () ... onde n é "rede" (ou seja, big-endian) e h é "Host" (ou seja, a endian'ness da máquina executando o código).

Estas aparentes 'funções' são (comumente) definidas como macros [veja <netinet/in.h>], portanto não há sobrecarga de tempo de execução para usá-las.

As seguintes macros usam essas 'funções' para avaliar endian'ness.

#include <arpa/inet.h>
#define  IS_BIG_ENDIAN     (1 == htons(1))
#define  IS_LITTLE_ENDIAN  (!IS_BIG_ENDIAN)

Além do que, além do mais:

A única vez que eu preciso saber o endian'ness de um sistema é quando eu escrevo uma variável [para um arquivo/outro] que pode ser lido por outro sistema de endianidade desconhecida (para compatibilidade entre plataformas ) ... Em casos como esses, você pode preferir usar diretamente as funções endian:

#include <arpa/inet.h>

#define JPEG_MAGIC  (('J'<<24) | ('F'<<16) | ('I'<<8) | 'F')

// Result will be in 'Host' byte-order
unsigned long  jpeg_magic = JPEG_MAGIC;

// Result will be in 'network' byte-order (IE. Big-Endian/Human-Readable)
unsigned long  jpeg_magic = htonl(JPEG_MAGIC);
7
BlueChip

Use uma função embutida em vez de uma macro. Além disso, você precisa armazenar algo na memória, que é um efeito colateral não tão bom de uma macro.

Você pode convertê-lo em uma macro curta usando uma variável estática ou global, como esta:

static int s_endianess = 0;
#define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)
6
user231967

Embora não haja #define portátil ou algo em que se possa confiar, as plataformas fornecem funções padrão para conversão de e para o seu 'Host' endian.

Geralmente, você faz armazenamento - em disco ou rede - usando 'endian de rede', que é BIG endian, e computação local usando o host endian (que em x86 é LITTLE endian). Você usa htons() e ntohs() e amigos para converter entre os dois.

5
Will

Não se esqueça que endianness não é toda a história - o tamanho de char pode não ser 8 bits (por exemplo, DSP), negação de complemento de dois não é garantida (por exemplo, Cray), alinhamento estrito pode ser necessário (por exemplo, SPARC, também ARM entra em middle-endian quando desalinhado), etc, etc.

Pode ser uma ideia melhor segmentar uma arquitetura CPU específica.

Por exemplo:

#if defined(__i386__) || defined(_M_IX86) || defined(_M_IX64)
  #define USE_LITTLE_ENDIAN_IMPL
#endif

void my_func()
{
#ifdef USE_LITTLE_ENDIAN_IMPL
  // Intel x86-optimized, LE implementation
#else
  // slow but safe implementation
#endif
}

Note que esta solução também não é ultra-portátil, infelizmente, pois depende de definições específicas do compilador (não há padrão, mas aqui uma compilação Agradável de tais definições).

3
rustyx
#include <stdint.h>
#define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8)
#define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)
3
user1207334

Tente isto:

#include<stdio.h>        
int x=1;
#define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian")
int main()
{

   TEST;
}
3
Prasoon Saurav

Por favor, preste atenção que a maioria das respostas aqui não são portáveis, já que os compiladores hoje avaliarão essas respostas em tempo de compilação (depende da otimização) e retornarão um valor específico baseado em um endianness específico, enquanto o endianness real da máquina pode diferir. Os valores nos quais o endianness é testado, nunca atingirão a memória do sistema, portanto, o código real executado retornará o mesmo resultado, independentemente do endianness real.

Para example , em ARM Cortex-M3, o endianness implementado refletirá em um bit de status AIRCR.ENDIANNESS e o compilador não poderá saber esse valor em tempo de compilação.

Saída de compilação para algumas das respostas sugeridas aqui:

https://godbolt.org/z/GJGNE2 para this answer,

https://godbolt.org/z/Yv-pyJ para this answer e assim por diante.

Para resolvê-lo, você precisará usar o qualificador volatile. A resposta de Yogeesh H T é a mais próxima para o uso atual da vida real, mas como Christoph sugere uma solução mais abrangente, uma ligeira correção em sua answer completaria a resposta, basta adicionar volatile à declaração de união: static const volatile union

Isso garantiria o armazenamento e a leitura da memória, necessária para determinar o endianness. 

1
user2162550

Código C para verificar se um sistema é little-endian ou big-indian.

int i = 7;
char* pc = (char*)(&i);
if (pc[0] == '\x7') // aliasing through char is ok
    puts("This system is little-endian");
else
    puts("This system is big-endian");
0
S. M. AMRAN

Minha resposta não é como pedimos, mas é muito simples de encontrar se seu sistema é little endian ou big endian?

Código:

#include<stdio.h>

int main()
{
  int a = 1;
  char *b;

  b = (char *)&a;
  if (*b)
    printf("Little Endian\n");
  else
    printf("Big Endian\n");
}
0
roottraveller