Detectando Endianness

Actualmente estoy tratando de crear un código fuente en C que maneje correctamente la E / S independientemente de la endianidad del sistema de destino.

Seleccioné “little endian” como mi convención de E / S, lo que significa que, para la CPU de Big Endian, necesito convertir datos mientras escribo o leo.

La conversión no es el problema. El problema al que me enfrento es detectar endianness, preferiblemente en tiempo de comstackción (ya que la CPU no cambia el endianness en el medio de la ejecución …).

Hasta ahora, he estado usando esto:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ... #else ... #endif 

Está documentado como una macro predefinida de GCC, y Visual parece entenderlo también.

Sin embargo, recibí un informe de que la verificación falla para algunos sistemas big_endian (PowerPC).

Por lo tanto, estoy buscando una solución infalible que garantice que la endianess se detecta correctamente, independientemente del comstackdor y el sistema de destino. bueno, la mayoría de ellos al menos …

[Editar]: la mayoría de las soluciones propuestas se basan en “pruebas de tiempo de ejecución”. Estas pruebas algunas veces pueden ser evaluadas adecuadamente por los comstackdores durante la comstackción y, por lo tanto, no cuestan un rendimiento real en el tiempo de ejecución.

Sin embargo, la bifurcación con algún tipo de << if (0) { ... } else { ... } >> no es suficiente. En la implementación del código actual, la statement de variables y funciones depende de la detección big_endian. Estos no se pueden cambiar con una instrucción if.

Bueno, obviamente, hay un plan alternativo, que es reescribir el código …

Preferiría evitar eso, pero, bueno, parece una esperanza cada vez menor …

[Editar 2]: He probado “pruebas de tiempo de ejecución”, modificando profundamente el código. Aunque hacen su trabajo correctamente, estas pruebas también afectan el rendimiento.

Esperaba que, dado que las pruebas tienen resultados predecibles, el comstackdor podría eliminar las twigs malas. Pero desafortunadamente, no funciona todo el tiempo. MSVC es un buen comstackdor y tiene éxito en la eliminación de twigs defectuosas, pero GCC tiene resultados mixtos, según las versiones, el tipo de pruebas y con mayor impacto en 64 bits que en 32 bits.

Es extraño. Y también significa que las pruebas en tiempo de ejecución no pueden garantizarse para ser tratadas por el comstackdor.

Edición 3 : En estos días, estoy usando una unión constante en tiempo de comstackción, esperando que el comstackdor la resuelva con una clara señal de sí / no. Y funciona bastante bien: https://godbolt.org/g/DAafKo

En el momento de la comstackción en C no puede hacer mucho más que confiar en el predecesor #define s, y no hay soluciones estándar porque el estándar C no se preocupa por la endianidad.

Aún así, podría agregar una afirmación que se hace en tiempo de ejecución al inicio del progtwig para asegurarse de que la suposición hecha al comstackr fue verdadera:

 inline int IsBigEndian() { int i=1; return ! *((char *)&i); } /* ... */ #ifdef COMPILED_FOR_BIG_ENDIAN assert(IsBigEndian()); #elif COMPILED_FOR_LITTLE_ENDIAN assert(!IsBigEndian()); #else #error "No endianness macro defined" #endif 

(donde COMPILED_FOR_BIG_ENDIAN y COMPILED_FOR_LITTLE_ENDIAN son macros #define d previamente de acuerdo con sus controles de endualidad del preprocesador)

En lugar de buscar una verificación en tiempo de comstackción, ¿por qué no simplemente utilizar la orden big-endian (que muchos consideran la “orden de red” ) y usar las htons / htonl / ntohs / ntohl proporcionadas por la mayoría de los sistemas UNIX y Windows. Ya están definidos para hacer el trabajo que estás tratando de hacer. ¿Por qué reinventar la rueda?

Como se dijo anteriormente, la única forma “real” de detectar Big Endian es usar pruebas de tiempo de ejecución.

Sin embargo, a veces, una macro puede ser preferida.

Lamentablemente, no he encontrado una sola “prueba” para detectar esta situación, sino una colección de ellos.

Por ejemplo, GCC recomienda: __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ . Sin embargo, esto solo funciona con las versiones más recientes, y las versiones anteriores (y otros comstackdores) darán a esta prueba un valor falso “verdadero”, ya que NULL == NULL. Por lo tanto, necesita la versión más completa: defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)

OK, ahora esto funciona para el GCC más nuevo, pero ¿y otros comstackdores?

Puede probar __BIG_ENDIAN__ o __BIG_ENDIAN o _BIG_ENDIAN que a menudo se definen en los comstackdores de big endian.

Esto mejorará la detección. Pero si se dirige específicamente a plataformas PowerPC, puede agregar algunas pruebas más para mejorar aún más la detección. Pruebe _ARCH_PPC o __PPC__ o __PPC o PPC o __powerpc__ o __powerpc o incluso powerpc . Vincula todas estas definiciones juntas, y tienes una oportunidad bastante justa para detectar sistemas big endian, y powerpc en particular, cualquiera que sea el comstackdor y su versión.

Por lo tanto, para resumir, no existe una “macros predefinidas estándar” que garantice la detección de CPU big-endian en todas las plataformas y comstackdores, pero hay muchas macros predefinidas que, en conjunto, dan una alta probabilidad de detectar correctamente a Big Endian en la mayoría de las circunstancias.

A pesar de las macros definidas por el comstackdor, no creo que haya una manera de tiempo de comstackción para detectar esto, ya que la determinación de la endianidad de una architecture implica analizar la manera en que almacena los datos en la memoria.

Aquí hay una función que hace precisamente eso:

 bool IsLittleEndian () { int i=1; return (int)*((unsigned char *)&i)==1; } 

Pruebe algo como:

 if(*(char *)(int[]){1}) { /* little endian code */ } else { /* big endian code */ } 

y vea si su comstackdor lo resuelve en tiempo de comstackción. De lo contrario, es posible que tenga más suerte haciendo lo mismo con una unión. En realidad, me gusta definir macros usando uniones que evalúen a 0,1 o 1,0 (respectivamente) para que pueda hacer cosas como acceder a buf[HI] y buf[LO] .

Como han señalado otros, no existe una forma portátil de verificar el endianness en tiempo de comstackción. Sin embargo, una opción sería utilizar la herramienta autoconf como parte de su script de comstackción para detectar si el sistema es big-endian o little-endian, y luego usar la macro AC_C_BIGENDIAN , que contiene esta información. En cierto sentido, esto construye un progtwig que detecta en tiempo de ejecución si el sistema es big-endian o little-endian, luego tiene esa información de salida del progtwig que luego puede ser usada estáticamente por el código fuente principal.

¡Espero que esto ayude!

No puede detectarlo en tiempo de comstackción para ser portable en todos los comstackdores. Tal vez puedas cambiar el código para hacerlo en tiempo de ejecución; esto es posible.

No es posible detectar la endianidad de forma portátil en C con las directivas de preprocesador.

Esto viene de p. 45 de Punteros en C :

 #include  #define BIG_ENDIAN 0 #define LITTLE_ENDIAN 1 int endian() { short int word = 0x0001; char *byte = (char *) &word; return (byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN); } int main(int argc, char* argv[]) { int value; value = endian(); if (value == 1) printf("The machine is Little Endian\n"); else printf("The machine is Big Endian\n"); return 0; } 

Sé que llego tarde a esta fiesta, pero esta es mi opinión.

 int is_big_endian() { return 1 & *(uint16_t*)"01"; } 

Esto se basa en el hecho de que '0' es 48 en decimal y '1' 49, por lo que '1' tiene el bit LSB establecido, mientras que '0' no. Podría convertirlos en '\x00' y '\x01' pero creo que mi versión lo hace más legible.

La función ntohl de Socket se puede usar para este propósito. Fuente

 // Soner #include  int main() { if (ntohl(0x12345678) == 0x12345678) { printf("big-endian\n"); } else if (ntohl(0x12345678) == 0x78563412) { printf("little-endian\n"); } else { printf("(stupid)-middle-endian\n"); } return 0; } 
 #define BIG_ENDIAN ((1 >> 1 == 0) ? 0 : 1)