C Definición de macro para determinar big endian o little endian machine?
architecture macros (18)
C Código para verificar si un sistema es little-endian o 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");
¿Hay una definición de macro de una línea para determinar la endianidad de la máquina? Estoy usando el siguiente código pero convertirlo a macro sería demasiado largo.
unsigned char test_endian( void )
{
int test_var = 1;
unsigned char test_endian* = (unsigned char*)&test_var;
return (test_endian[0] == NULL);
}
Código que admite órdenes de bytes arbitrarios, listo para colocarse en un archivo llamado 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
Verificaría pequeños sistemas de endian a través de
O32_HOST_ORDER == O32_LITTLE_ENDIAN
Creo que esto es lo que se pidió. Solo probé esto en una pequeña máquina endian bajo msvc. Alguien puede confirmar en una gran máquina 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 nota al margen (específica del compilador), con un compilador agresivo puede usar la optimización de "eliminación de código #if
" para lograr el mismo efecto que un tiempo de compilación #if
así:
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; // ?
}
Lo anterior se basa en el hecho de que el compilador reconoce los valores constantes en tiempo de compilación, elimina por completo el código dentro de if (false) { ... }
y reemplaza el código como if (true) { foo(); }
if (true) { foo(); }
con foo();
El peor de los casos: el compilador no realiza la optimización, aún obtiene el código correcto pero un poco más lento.
De hecho, puede acceder a la memoria de un objeto temporal utilizando un literal compuesto (C99):
#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})
Qué GCC evaluará en tiempo de compilación.
La ''biblioteca de red C'' ofrece funciones para manejar la endianidad. Es decir, htons (), htonl (), ntohs () y ntohl () ... donde n es "red" (es decir, big-endian) y h es "host" (es decir, el endianness de la máquina que ejecuta el código).
Estas "funciones" aparentes (comúnmente) se definen como macros [ver <netinet / in.h>], por lo que no hay sobrecarga de tiempo de ejecución para usarlas.
Las siguientes macros usan estas ''funciones'' para evaluar el endianismo.
#include <arpa/inet.h>
#define IS_BIG_ENDIAN (1 == htons(1))
#define IS_LITTLE_ENDIAN (!IS_BIG_ENDIAN)
En adición:
La única vez que necesito saber el carácter endógeno de un sistema es cuando escribo una variable [en un archivo / otro] que puede ser leída por otro sistema de identidad desconocida (para la compatibilidad multiplataforma) ) ... En casos como estos, puede preferir usar las funciones endian directamente:
#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);
Macro para encontrar endiannes
#define ENDIANNES() ((1 && 1 == 0) ? printf("Big-Endian"):printf("Little-Endian"))
o
#include <stdio.h>
#define ENDIAN() { /
volatile unsigned long ul = 1;/
volatile unsigned char *p;/
p = (volatile unsigned char *)&ul;/
if (*p == 1)/
puts("Little endian.");/
else if (*(p+(sizeof(unsigned long)-1)) == 1)/
puts("Big endian.");/
else puts("Unknown endian.");/
}
int main(void)
{
ENDIAN();
return 0;
}
Mi respuesta no es la que se le preguntó, pero ¿es realmente simple encontrar si su sistema es pequeño endian o 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");
}
No existe un estándar, pero en muchos sistemas, incluido <endian.h>
le dará algunas definiciones para buscar.
No olvide que la endianidad no es toda la historia: el tamaño del char
puede no ser de 8 bits (p. Ej., DSP), la negación del complemento de dos no está garantizada (por ejemplo, Cray), puede ser necesaria una alineación estricta (por ejemplo, SPARC; middle-endian cuando no está alineado), etc., etc.
En su lugar, podría ser una mejor idea apuntar a una arquitectura de CPU específica.
Por ejemplo:
#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
}
Tenga en cuenta que esta solución tampoco es ultraportátil, lamentablemente, ya que depende de las definiciones específicas del compilador (no hay un estándar, pero here''s una buena compilación de tales definiciones).
Para detectar endianness en tiempo de ejecución, debe poder referirse a la memoria. Si se apega al estándar C, declarar una variable en la memoria requiere una declaración, pero devolver un valor requiere una expresión. No sé cómo hacer esto en una sola macro, esta es la razón por la cual gcc tiene extensiones :-)
Si está dispuesto a tener un archivo .h, puede definir
static uint32_t endianness = 0xdeadbeef;
enum endianness { BIG, LITTLE };
#define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE /
: *(const char *)&endianness == 0xde ? BIG /
: assert(0))
y luego puedes usar la macro ENDIANNESS
como quieras.
Prueba esto:
#include<stdio.h>
int x=1;
#define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian")
int main()
{
TEST;
}
Si bien no existe #define portátil o algo en lo que confiar, las plataformas sí ofrecen funciones estándar para la conversión hacia y desde su endian ''host''.
En general, usted hace almacenamiento, en el disco, o en la red, usando ''endian de la red'', que es BIG endian, y computación local usando el host endian (que en x86 es LITTLE endian). Utiliza htons()
y ntohs()
y amigos para convertir entre los dos.
Si está buscando una prueba de tiempo de compilación y está utilizando gcc, puede hacer:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
Vea la documentación de gcc para más información.
Si solo desea confiar en el preprocesador, debe averiguar la lista de símbolos predefinidos. La aritmética preprocesadora no tiene un concepto de direccionamiento.
GCC en Mac define __LITTLE_ENDIAN__
o __BIG_ENDIAN__
$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1
Luego, puede agregar más directivas condicionales de preprocesador basadas en detección de plataformas como #ifdef _WIN32
etc.
Si tiene un compilador que admita literales compuestos C99:
#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})
o:
#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)
Sin embargo, en general, debe intentar escribir un código que no dependa de la endianidad de la plataforma de host.
Ejemplo de implementación independiente de host-endianness de ntohl()
:
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];
}
Silencio tarde pero ... Si absolutamente debe tener un código macro Y ultraportátil, detecte y configúrelo desde su entorno construido (cmake / autotools).
Aquí hay un programa simple para lograrlo, adecuado para la reducción:
#if __STDC_VERSION__ < 199901L
#error "Requires C99 compatibility"
#endif
#include <stdint.h>
#include <stdio.h>
const char MAGIC[4] = {0xDE, 0xAD, 0xBE, 0xEF};
int
main(void) {
uint32_t magical = *(const uint32_t *)MAGIC;
switch(magical) {
case 0xEFBEADDE: printf("little/n"); break;
case 0xDEADBEEF: printf("big/n"); break;
case 0xADDEEFBE: printf("pdp/n"); break;
default: for(; magical; magical >>= 8) {
switch(magical & 0xff) {
case 0xDE: printf("3"); break;
case 0xAD: printf("2"); break;
case 0xBE: printf("1"); break;
default: printf("0"); }
} printf("/n");}
return (0);
}
Use una función en línea en lugar de una macro. Además, debe almacenar algo en la memoria, que es un efecto secundario no tan agradable de una macro.
Puede convertirlo a una macro corta usando una variable estática o global, como esta:
static int s_endianess = 0;
#define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)
#include <stdint.h>
#define IS_LITTLE_ENDIAN (*(uint16_t*)"/0/1">>8)
#define IS_BIG_ENDIAN (*(uint16_t*)"/1/0">>8)