Determinación de endianidad en tiempo de comstackción

¿Existe una manera segura y portátil de determinar (durante el tiempo de comstackción) la endianidad de la plataforma en la que se está comstackndo mi progtwig? Estoy escribiendo en C.

[EDITAR] ¡Gracias por las respuestas, decidí seguir con la solución de tiempo de ejecución!

Esto es para verificar el tiempo de comstackción

Puede usar información del archivo de encabezado boost endian.hpp , que cubre muchas plataformas.

editar para verificar el tiempo de ejecución

 bool isLittleEndian() { short int number = 0x1; char *numPtr = (char*)&number; return (numPtr[0] == 1); } 

Crea un entero y lee su primer byte (byte menos significativo). Si ese byte es 1, entonces el sistema es poco endian, de lo contrario es big endian.

editar Pensando en eso

Sí, podría encontrarse con un problema potencial en algunas plataformas (no se puede pensar en ninguna) donde sizeof(char) == sizeof(short int) . Puede usar los tipos integrales de múltiples bytes de ancho fijo disponibles en , o si su plataforma no lo tiene, de nuevo puede adaptar un encabezado de refuerzo para su uso: stdint.hpp

Para responder la pregunta original de una verificación en tiempo de comstackción , no existe una forma estandarizada de hacerlo que funcione en todos los comstackdores actuales y futuros, porque ninguno de los estándares existentes de C, C ++ y POSIX definen macros para detectar endianness.

Pero, si está dispuesto a limitarse a un conjunto conocido de comstackdores, puede buscar cada una de las comstackciones de esos comstackdores para averiguar qué macros predefinidas (si las hay) utilizan para definir endianness. Esta página enumera varias macros que puedes buscar, así que aquí hay un código que funcionaría para ellas:

 #if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \ defined(__BIG_ENDIAN__) || \ defined(__ARMEB__) || \ defined(__THUMBEB__) || \ defined(__AARCH64EB__) || \ defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) // It's a big-endian target architecture #elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \ defined(__LITTLE_ENDIAN__) || \ defined(__ARMEL__) || \ defined(__THUMBEL__) || \ defined(__AARCH64EL__) || \ defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) // It's a little-endian target architecture #else #error "I don't know what architecture this is!" #endif 

Si no puede encontrar las macros predefinidas que su comstackdor usa en su documentación, también puede intentar obligarlo a escupir su lista completa de macros predefinidas y adivinar de allí qué funcionará (busque cualquier cosa con ENDIAN, ORDER o el procesador). nombre de la architecture en él). Esta página enumera una serie de métodos para hacerlo en diferentes comstackdores:

 Compiler C macros C++ macros Clang/LLVM clang -dM -E -xc /dev/null clang++ -dM -E -x c++ /dev/null GNU GCC/G++ gcc -dM -E -xc /dev/null g++ -dM -E -x c++ /dev/null Hewlett-Packard C/aC++ cc -dM -E -xc /dev/null aCC -dM -E -x c++ /dev/null IBM XL C/C++ xlc -qshowmacros -E /dev/null xlc++ -qshowmacros -E /dev/null Intel ICC/ICPC icc -dM -E -xc /dev/null icpc -dM -E -x c++ /dev/null Microsoft Visual Studio (none) (none) Oracle Solaris Studio cc -xdumpmacros -E /dev/null CC -xdumpmacros -E /dev/null Portland Group PGCC/PGCPP pgcc -dM -E (none) 

Finalmente, para completarlo, los comstackdores de Microsoft Visual C / C ++ son los extraños y no tienen ninguno de los anteriores. Afortunadamente, han documentado sus macros predefinidas aquí , y puede usar la architecture del procesador de destino para inferir la endianidad. Si bien todos los procesadores soportados actualmente en Windows son little-endian ( _M_IX86 , _M_X64 , _M_IA64 y _M_ARM son little-endian), algunos procesadores históricamente compatibles como PowerPC ( _M_PPC ) eran big-endian. Pero, lo que es más relevante, la Xbox 360 es una máquina PowerPC de gran tamaño, por lo que si está escribiendo un encabezado de biblioteca multiplataforma, no tiene sentido buscar _M_PPC .

Con C99, puede realizar el control como:

 #define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c) 

Los condicionales como if (I_AM_LITTLE) se evaluarán en tiempo de comstackción y permitirán que el comstackdor optimice bloques enteros.

No tengo la referencia de inmediato para ver si esto es estrictamente una expresión constante en C99 (que permitiría su uso en los inicializadores para datos de duración de almacenamiento estático), pero si no, es la mejor opción.

Interesante lectura de la C Preguntas frecuentes :

Probablemente no puedas. Las técnicas usuales para detectar endianness involucran punteros o matrices de char, o tal vez uniones, pero la aritmética del preprocesador solo usa números enteros largos, y no hay un concepto de direccionamiento. Otra posibilidad tentadora es algo así como

  #if 'ABCD' == 0x41424344 

pero esto tampoco es confiable.

Me gustaría extender las respuestas para proporcionar una función constexpr para C ++

 union Mix { int sdat; char cdat[4]; }; static constexpr Mix mix { 0x1 }; constexpr bool isLittleEndian() { return mix.cdat[0] == 1; } 

Como la mix es constexpr , es tiempo de comstackción y se puede usar en constexpr bool isLittleEndian() . Debe ser seguro de usar.

Actualizar

Como @Cheersandhth señaló a continuación, estos parecen ser problemáticos.

La razón es que no es C ++ 11-Estándar conforme , donde el tipo de juego de palabras está prohibido. Siempre puede haber un solo miembro de la unión activo a la vez. Con un comstackdor estándar compatible, obtendrá un error.

Entonces, no lo use en C ++. Parece que puedes hacerlo en C sin embargo. Dejo mi respuesta con fines educativos 🙂 y porque la pregunta es sobre C …

No durante el tiempo de comstackción, pero quizás durante el tiempo de ejecución. Aquí hay una función de C que escribí para determinar la endianidad:

 /* Returns 1 if LITTLE-ENDIAN or 0 if BIG-ENDIAN */ #include  int endianness() { union { uint8_t c[4]; uint32_t i; } data; data.i = 0x12345678; return (data.c[0] == 0x78); } 

Una vez usé una construcción como esta:

 uint16_t HI_BYTE = 0, LO_BYTE = 1; uint16_t s = 1; if(*(uint8_t *) &s == 1) { HI_BYTE = 1; LO_BYTE = 0; } pByte[HI_BYTE] = 0x10; pByte[LO_BYTE] = 0x20; 

gcc con -O2 fue capaz de completar el tiempo de comstackción. Esto significa que las variables HI_BYTE y LO_BYTE se reemplazaron por completo e incluso el acceso pByte fue reemplazado en el ensamblador por el equivalente de *(unit16_t *pByte) = 0x1020; .

Es el tiempo de comstackción que se necesita.

Desde Finalmente, detección de endianness de una línea en el preprocesador C :

 #include  #define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100) 

Cualquier optimizador decente resolverá esto en tiempo de comstackción. gcc lo hace a -O1 .

Por supuesto, stdint.h es C99. Para la portabilidad ANSI / C89, vea la biblioteca Instant C9x de Doug Gwyn.

Que yo sepa, no, no durante el tiempo de comstackción.

En tiempo de ejecución, puede hacer comprobaciones triviales, como establecer un valor de varios bytes en una cadena de bits conocida e inspeccionar los bytes que resultan. Por ejemplo, al usar una unión,

 typedef union { uint32_t word; uint8_t bytes[4]; } byte_check; 

o casting,

 uint32_t word; uint8_t * bytes = &word; 

Tenga en cuenta que para las verificaciones de endianness completamente portátiles, debe tener en cuenta los sistemas big-endian, little-endian y mixed-endian.

EDIT2: Este método no funciona. La representación de la constante multibyte es específica del comstackdor / plataforma y no se puede usar de manera confiable. El enlace interjay dio ( http://www.ideone.com/LaKpj ) da un ejemplo donde falla. En Solaris / SPARC, el mismo comstackdor gcc 4.3.3 da la respuesta correcta, pero el comstackdor SUNStudio 12 tendrá el mismo comportamiento que el gcc 4.3.4 en x86 utilizado en ese enlace.

Entonces, podemos concluir, todavía no hay un buen uso del carácter multibyte

Encontré este nuevo método que tiene la ventaja de ser simple y comstackr el tiempo.

 switch('AB') { case 0x4142: printf("ASCII Big endian\n"); break; case 0x4241: printf("ASCII Little endian\n"); break; case 0xC1C2: printf("EBCDIC Big endian\n"); break; case 0xC2C1: printf("EBCDIC Little endian\n"); break; } 

EDITAR:

Encontré incluso una forma de hacerlo en el pre-procesador:

 #if 'AB' == 0x4142 #error "ASCII Big endian\n" #elif 'AB' == 0x4241 #error "ASCII Little endian\n" #elif 'AB' == 0xC1C2 #error "EBCDIC Big endian\n" #elif 'AB' == 0xC2C1 #error "EBCDIC Little endian\n" #else #error "unknown coding and endianness\n" #endif 

Y antes de que alguien pregunte, las constantes de caracteres multibyte son ANSI-C (incluso C90) pero están definidas por la implementación. Esta es la primera aplicación útil que he encontrado para ellos.