Detectando endianness programmatically en un progtwig de C ++

¿Hay alguna manera programática de detectar si estás o no en una architecture big-endian o little-endian? Necesito poder escribir código que se ejecutará en un sistema Intel o PPC y usará exactamente el mismo código (es decir, sin comstackción condicional).

No me gusta el método basado en el tipo de juego de palabras: a menudo el comstackdor lo advierte. ¡Para eso están los sindicatos!

int is_big_endian(void) { union { uint32_t i; char c[4]; } bint = {0x01020304}; return bint.c[0] == 1; } 

El principio es equivalente al tipo de caso sugerido por otros, pero esto es más claro, y de acuerdo con C99, se garantiza que es correcto. gcc prefiere esto comparado con el lanzamiento directo del puntero.

Esto también es mucho mejor que arreglar el endianness en tiempo de comstackción. Para el sistema operativo que admite architecture múltiple (binario grueso en Mac OS x por ejemplo), esto funcionará para ppc / i386, mientras que es muy fácil estropear las cosas .

Puede hacerlo configurando un int y enmascarando bits, pero probablemente la forma más fácil es simplemente usar las operaciones de conversión de byte de red incorporadas (ya que el orden de bytes de la red siempre es big endian).

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

El toqueteo de bits podría ser más rápido, pero de esta manera es simple, directo y bastante imposible de perder.

Por favor mira este artículo :

Aquí hay un código para determinar cuál es el tipo de su máquina

 int num = 1; if(*(char *)&num == 1) { printf("\nLittle-Endian\n"); } else { printf("Big-Endian\n"); } 

Esto normalmente se realiza en tiempo de comstackción (especialmente por razones de rendimiento) utilizando los archivos de encabezado disponibles del comstackdor o crea los tuyos propios. En Linux tiene el archivo de encabezado “/usr/include/endian.h”

Puede usar std::endian si tiene acceso al comstackdor C ++ 20 como GCC 8+ o Clang 7+:

 #include  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 } 

Ehm … Me sorprende que nadie se haya dado cuenta de que el comstackdor simplemente optimizará la prueba, y pondrá un resultado fijo como valor de retorno. Esto representa todos los ejemplos de código anteriores, efectivamente inútiles. ¡Lo único que se devolverá es la endianidad en tiempo de comstackción! Y sí, probé todos los ejemplos anteriores. Aquí hay un ejemplo con MSVC 9.0 (Visual Studio 2008).

Código Pure C

 int32 DNA_GetEndianness(void) { union { uint8 c[4]; uint32 i; } u; ui = 0x01020304; if (0x04 == uc[0]) return DNA_ENDIAN_LITTLE; else if (0x01 == uc[0]) return DNA_ENDIAN_BIG; else return DNA_ENDIAN_UNKNOWN; } 

Desassembly

 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 : ui = 1; ; 18 : ; 19 : if (1 == uc[0]) ; 20 : return DNA_ENDIAN_LITTLE; mov eax, 1 ; 21 : else if (1 == uc[3]) ; 22 : return DNA_ENDIAN_BIG; ; 23 : else ; 24 : return DNA_ENDIAN_UNKNOWN; ; 25 : } ret _DNA_GetEndianness ENDP END 

Quizás es posible desactivar CUALQUIER optimización en tiempo de comstackción solo para esta función, pero no sé. De lo contrario, es posible codificarlo en el ensamblaje, aunque eso no es portátil. E incluso entonces, incluso eso podría optimizarse. Me hace pensar que necesito un ensamblador realmente malo, implementar el mismo código para todas las CPU / conjuntos de instrucciones existentes, y bueno … no importa.

Además, alguien aquí dijo que la endianidad no cambia durante el tiempo de ejecución. INCORRECTO. Hay máquinas bi-endian por ahí. Su endianidad puede variar durante la ejecución. TAMBIÉN, no solo hay Little Endian y Big Endian, sino también otras endianzas (qué palabra).

Odio y amo la encoding al mismo tiempo …

Declare una variable int:

 int variable = 0xFF; 

Ahora use los caracteres char * en varias partes y verifique qué hay en esas partes.

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

Dependiendo de cuál apunta a 0xFF byte ahora puede detectar endianness. Esto requiere sizeof (int)> sizeof (char), pero definitivamente es cierto para las plataformas discutidas.

Me sorprendió que nadie haya mencionado las macros que el pre-procesador define por defecto. Si bien estos variarán según su plataforma; son mucho más limpios que tener que escribir tu propio endian-check.

Por ejemplo; si miramos las macros integradas que define GCC (en una máquina X86-64):

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

En una máquina PPC obtengo:

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

(El :| gcc -dM -E -xc - magic imprime todas las macros incorporadas).

Para obtener más detalles, es posible que desee consultar este artículo del proyecto de código Conceptos básicos sobre Endianness :

¿Cómo probar dinámicamente para el tipo Endian en tiempo de ejecución?

Como se explicó en Preguntas frecuentes sobre la animación por computadora, puede usar la siguiente función para ver si su código se está ejecutando en un sistema Little-o Big-Endian: Contraer

 #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 asigna el valor 0001h a un entero de 16 bits. A continuación, se asigna un puntero a punto en el primer byte (menos significativo) del valor entero. Si el primer byte del entero es 0x01h, entonces el sistema es Little-Endian (el 0x01h está en la dirección más baja o menos significativa). Si es 0x00h, entonces el sistema es Big-Endian.

Como se indicó anteriormente, use trucos sindicales.

Sin embargo, hay algunos problemas con los recomendados anteriormente, sobre todo que el acceso a memoria no alineado es notoriamente lento para la mayoría de las architectures, y algunos comstackdores no reconocerán tales predicados constantes, a menos que estén alineados.

Como la mera prueba endian es aburrida, aquí va la función (plantilla) que volteará la entrada / salida del entero arbitrario de acuerdo con su especificación, independientemente de la architecture del host.

 #include  #define BIG_ENDIAN 1 #define LITTLE_ENDIAN 0 template  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 convertir de endian determinado a host, use:

host = endian(source, endian_of_source)

Para convertir del host endian al endian dado, use:

output = endian(hostsource, endian_you_want_to_output)

El código resultante es tan rápido como escribir ensamblaje de mano en clang, en gcc es un poco más lento (desenrollado y, <<, >>, | para cada byte) pero aún decente.

A menos que esté utilizando un marco que haya sido portado a procesadores PPC e Intel, tendrá que hacer comstackciones condicionales, ya que las plataformas PPC e Intel tienen architectures de hardware, tuberías, buses, etc. completamente diferentes. Esto hace que el código ensamblador sea completamente diferente los dos.

En cuanto a encontrar endianness, haz lo siguiente:

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

Obtendrá tempChar de 0x12 o 0x34, de lo que sabrá la endianidad.

La forma en C ++ ha sido usar boost , donde los controles y moldes del preprocesador se dividen en compartimentos en bibliotecas muy bien probadas.

La Biblioteca Predef (boost / predef.h) reconoce cuatro tipos diferentes de endianness .

La Biblioteca Endian se planificó para enviarse al estándar C ++ y admite una amplia variedad de operaciones en datos sensibles a endian.

Como se indica en las respuestas anteriores, Endianness será una parte de c ++ 20.

Haría algo como esto:

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

En esta línea, obtendría una función de tiempo eficiente que solo hace el cálculo una vez.

 bool isBigEndian() { static const uint16_t m_endianCheck(0x00ff); return ( *((uint8_t*)&m_endianCheck) == 0x0); } 

compile time, non-macro, C ++ 11 constexpr solution:

 union { uint16_t s; unsigned char c[2]; } constexpr static d {1}; constexpr bool is_little_endian() { return dc[0] == 1; } 
 union { int i; char c[sizeof(int)]; } x; xi = 1; if(xc[0] == 1) printf("little-endian\n"); else printf("big-endian\n"); 

Esta es otra solución. Similar a la solución de Andrew Hare.

no probado, pero en mi opinión, esto debería funcionar? porque será 0x01 en little endian, y 0x00 en big endian?

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

También puede hacerlo a través del preprocesador usando algo como el archivo de encabezado boost que se puede encontrar boost endian

 int i=1; char *c=(char*)&i; bool littleendian=c; 

¿Qué tal esto?

 #include  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; } 

A menos que el encabezado endian sea solo GCC, proporciona macros que puede 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!"); ... 

Si no quiere una comstackción condicional, puede escribir el código independiente Endian. Aquí hay un ejemplo (tomado de Rob Pike ):

Leyendo un entero almacenado en little-endian en el disco, de una manera endian independiente:

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

El mismo código, tratando de tener en cuenta el endianness de la 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 

Consulte Endianidad : ilustración del código de nivel C.

 // assuming target architecture is 32-bit = 4-Bytes enum ENDIANESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE }; ENDIANESS 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; } 

Aquí hay otra versión C. Define una macro llamada wicked_cast() para el wicked_cast() palabras en línea a través de los literales de unión C99 y el operador __typeof__ no estándar.

 #include  #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); } 

Si los enteros son valores de un solo byte, la endianidad no tiene sentido y se generará un error en tiempo de comstackción.

La forma en que los comstackdores de C (al menos todos los que conozco) trabajan en la endianidad debe decidirse en tiempo de comstackción. Incluso para procesadores biendianos (como ARM och MIPS), debe elegir endianness en tiempo de comstackción. Además, la endianidad se define en todos los formatos de archivo comunes para ejecutables (como ELF). Aunque es posible crear un blob binario de código biandian (¿para algún exploit de servidor ARM tal vez?) Probablemente deba hacerse en ensamblaje.

Estaba repasando el libro de texto: Sistema informático: la perspectiva de un progtwigdor , y hay un problema para determinar qué endian es esto por el progtwig C.

Usé la función del puntero para hacer eso de la siguiente manera:

 #include  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 int ocupa 4 bytes, y char ocupa solo 1 byte. Podríamos usar un puntero char para apuntar al int con el valor 1. Por lo tanto, si la computadora es poco endian, el carácter al que apunta el puntero char es con el valor 1, de lo contrario, su valor debería ser 0.

Como señaló Coriiander, la mayoría (si no todos) de esos códigos aquí se optimizarán en el tiempo de comstackción, por lo que los binarios generados no verificarán la “endianidad” en tiempo de ejecución.

Se ha observado que un ejecutable dado no debe ejecutarse en dos órdenes de bytes diferentes, pero no tengo idea de si ese es siempre el caso, y me parece un truco comprobar el tiempo de comstackción. Así que codifiqué esta función:

 #include  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; } 

MinGW no pudo optimizar este código, aunque optimiza los otros códigos aquí. Creo que es porque dejo el valor “aleatorio” alocated en la memoria de bytes más pequeños (como mínimo 7 de sus bits), por lo que el comstackdor no puede saber cuál es ese valor aleatorio y no optimiza la función de distancia.

También he codificado la función para que la verificación solo se realice una vez y el valor de retorno se almacene para las próximas pruebas.

Intereting Posts