Detectar endianness programáticamente en un programa C ++

211

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

Jay T
fuente
44
En aras de la exhaustividad, aquí hay un enlace a la pregunta de otra persona acerca de tratar de medir la endianidad (en tiempo de compilación): stackoverflow.com/questions/280162/…
Faisal Vali
14
¿Por qué no determinar la endianidad en tiempo de compilación? No puede cambiar en tiempo de ejecución.
ephemient
3
AFAIK, no hay una forma confiable y universal de hacerlo. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

Respuestas:

174

No me gusta el método basado en el tipo de juego de palabras: a menudo el compilador lo advertirá. ¡Para eso son exactamente los sindicatos!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

El principio es equivalente al caso tipo sugerido por otros, pero esto es más claro y, según C99, se garantiza que es correcto. gcc prefiere esto en comparación con el elenco de puntero directo.

Esto también es mucho mejor que arreglar el endianness en el momento de la compilación: para sistemas operativos que admiten arquitectura múltiple (binario gordo en Mac os x, por ejemplo), esto funcionará tanto para ppc / i386, mientras que de lo contrario es muy fácil estropear las cosas .

David Cournapeau
fuente
51
No recomiendo nombrar una variable "Bint" :)
MKB
42
¿Estás seguro de que esto está bien definido? En C ++ solo un miembro de la unión puede estar activo a la vez, es decir, no puede asignar usando un nombre de miembro y leer usando otro (aunque hay una excepción para estructuras compatibles con el diseño)
Faisal Vali
27
@Matt: busqué en Google, y bint parece tener un significado en inglés que no conocía :)
David Cournapeau
17
He probado esto, y tanto en gcc 4.0.1 como en gcc 4.4.1 el resultado de esta función se puede determinar en tiempo de compilación y tratar como una constante. Esto significa que el compilador caerá si las ramas que dependen únicamente del resultado de esta función y nunca se tomarán en la plataforma en cuestión. Esto probablemente no sea cierto para muchas implementaciones de htonl.
Omnifarious
66
¿Es esta solución realmente portátil? ¿Y si CHAR_BIT != 8?
zorgit
80

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

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

El violín podría ser más rápido, pero de esta manera es simple, directo y bastante imposible de estropear.

Eric Petroelje
fuente
1
Las operaciones de conversión de red también se pueden usar para convertir todo a Big Endian, resolviendo así otros problemas que Jay pueda estar encontrando.
Brian
66
@sharptooth: lento es un término relativo, pero sí, si la velocidad es realmente un problema, úselo una vez al inicio del programa y establezca una variable global con el endianness.
Eric Petroelje
55
htonl tiene otro problema: en algunas plataformas (¿ventanas?), no reside en la biblioteca de tiempo de ejecución C propiamente dicha, sino en bibliotecas adicionales relacionadas con la red (socket, etc.). Esto es un obstáculo para una sola función si no necesita la biblioteca de otra manera.
David Cournapeau el
77
Tenga en cuenta que en Linux (gcc), htonl está sujeto a un plegado constante en el momento de la compilación, por lo que una expresión de este formulario no tiene sobrecarga en tiempo de ejecución (es decir, se dobla constantemente a 1 o 0, y luego la eliminación del código muerto elimina el otra rama del if)
bdonlan
2
Además, en x86 htonl se puede implementar (y está en Linux / gcc) de manera muy eficiente utilizando un ensamblador en línea, especialmente si se dirige a una microarquitectura con soporte para la BSWAPoperación.
bdonlan
61

Por favor vea 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");
}
Andrew Hare
fuente
25
Tenga en cuenta que depende de que int y char sean de diferentes longitudes, lo cual es casi siempre el caso, pero no está garantizado.
David Thornley
10
He trabajado en sistemas embebidos donde short int y char tenían el mismo tamaño ... No recuerdo si el int regular también tenía ese tamaño (2 bytes) o no.
rmeador
2
¿Por qué ES ESTA respuesta prácticamente LA ÚNICA RESPUESTA que NO me hace pensar "amigo, qué estás haciendo?", que es el caso de la mayoría de las respuestas aquí: o
hanshenrik
2
@Shillard int debe ser al menos tan grande, ¡pero no hay ningún requisito en el estándar para que el carácter restringido se limite a menos! Si echa un vistazo a la familia TI F280x, descubrirá que CHAR_BIT es 16 y sizeof (int) == sizeof (char) mientras que los límites que menciona se mantienen absolutamente bien ...
Aconcagua
55
¿Por qué no usar uint8_t y uint16_t?
Rodrigo
58

Puede usarlo std::endiansi tiene acceso al compilador C ++ 20, como GCC 8+ o Clang 7+.

Nota: std::endiancomenzó en <type_traits>pero se mudó a <bit>la reunión de 2019 en Colonia. GCC 8, Clang 7, 8 y 9 lo tienen adentro <type_traits>mientras que GCC 9+ y Clang 10+ lo tienen adentro <bit>.

#include <bit>

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
}
Lyberta
fuente
55
Como todos, tengo acceso a borradores / propuestas de C ++ 17 y 20, pero, a partir de ahora, ¿existe algún compilador de C ++ 20?
Xeverous
@Xeverous Solo requiere enumeraciones de ámbito, por lo que sospecho que la mayoría de los proveedores lo agregarán a su implementación stdlib como uno de sus cambios anteriores.
Pharap
@Xeverous GCC 8 fue lanzado y lo admite.
Lyberta
De las más de 30 respuestas a la pregunta, esta parece ser la única, eso es completamente exacto (con otra respuesta que es al menos correcta en parte).
Inspectable el
40

Esto normalmente se realiza en tiempo de compilación (especialmente por razones de rendimiento) mediante el uso de los archivos de encabezado disponibles en el compilador o crea el tuyo propio. En Linux tiene el archivo de encabezado "/usr/include/endian.h"

cuenta
fuente
8
No puedo creer que esto no haya sido votado más alto. No es que el endianness vaya a cambiar bajo un programa compilado, por lo que nunca hay necesidad de una prueba de tiempo de ejecución.
Dolda2000
@ Dolda2000 Podría potencialmente, ver los modos endian ARM.
Tyzoid
10
@Tyzoid: No, un programa compilado siempre se ejecutará en el modo endian para el que fue compilado, incluso si el procesador es capaz de hacerlo.
Dolda2000
16

Me sorprendió que nadie haya mencionado las macros que el preprocesador define por defecto. Si bien estos variarán dependiendo de su plataforma; son mucho más limpios que tener que escribir su propio cheque endian.

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

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

En una máquina PPC obtengo:

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

(La :| gcc -dM -E -x c -magia imprime todas las macros integradas).

DaveR
fuente
77
Estas macros no se muestran consistentemente en absoluto. Por ejemplo, en gcc 4.4.5 del repositorio de Redhat 6, la ejecución echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'no devuelve nada, mientras que gcc 3.4.3 (de /usr/sfw/bintodos modos) en Solaris tiene una definición en este sentido. He visto problemas similares en VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4).
Brian Vandenberg
15

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

Código C puro

int32 DNA_GetEndianness(void)
{
    union 
    {
        uint8  c[4];
        uint32 i;
    } u;

    u.i = 0x01020304;

    if (0x04 == u.c[0])
        return DNA_ENDIAN_LITTLE;
    else if (0x01 == u.c[0])
        return DNA_ENDIAN_BIG;
    else
        return DNA_ENDIAN_UNKNOWN;
}

Desmontaje

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   :     u.i = 1;
; 18   : 
; 19   :     if (1 == u.c[0])
; 20   :         return DNA_ENDIAN_LITTLE;

    mov eax, 1

; 21   :     else if (1 == u.c[3])
; 22   :         return DNA_ENDIAN_BIG;
; 23   :     else
; 24   :        return DNA_ENDIAN_UNKNOWN;
; 25   : }

    ret
_DNA_GetEndianness ENDP
END

Quizás sea posible desactivar CUALQUIER optimización de tiempo de compilación solo para esta función, pero no lo 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 resistencia no cambia durante el tiempo de ejecución. INCORRECTO. Hay máquinas bi-endian por ahí. Su resistencia puede variar durante la ejecución. TAMBIÉN, no solo hay Little Endian y Big Endian, sino también otras endianidades (qué palabra).

Odio y amo la codificación al mismo tiempo ...

Cilantro
fuente
11
¿No tienes que volver a compilar para ejecutar en una plataforma diferente de todos modos?
bobobobo
2
Aunque funciona bien para MSVC, no lo hace para todas las versiones de GCC en todas las circunstancias. Por lo tanto, una "verificación en tiempo de ejecución" dentro de un ciclo crítico puede estar correctamente sin ramificar en tiempo de compilación, o no. No hay garantía del 100%.
Cian
21
No existe un procesador x86 big endian. Incluso si ejecuta Ubuntu en un procesador biendian (como ARM o MIPS), los ejecutables ELF siempre son endian grandes (MSB) o pequeños (LSB). No se pueden crear ejecutables biendianos, por lo que no se necesitan verificaciones de tiempo de ejecución.
Fabel
44
Para desactivar la optimización en el uso de este método de 'unión volátil ...' Se dice que el compilador 'U' se puede cambiar a otro lugar y los datos deben cargarse
mishmashru
1
Para que esta función devuelva un valor diferente en tiempo de ejecución que el optimizador está calculando que implicará que el optimizador tiene errores. ¿Está diciendo que hay ejemplos de código binario optimizado compilado que puede ejecutarse de forma portátil en dos arquitecturas diferentes de diferente endianness, a pesar de las suposiciones obvias hechas por el optimizador (en todo el programa) durante la compilación que parecen ser incompatibles con al menos uno de esos arquitecturas?
Scott
13

Declarar una variable int:

int variable = 0xFF;

Ahora use punteros char * para varias partes y verifique qué hay en esas partes.

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

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

diente filoso
fuente
8

Para más detalles, puede consultar este artículo del proyecto de código Conceptos básicos sobre Endianness :

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

Como se explica en las Preguntas frecuentes sobre Animación por computadora, puede usar la siguiente función para ver si su código se ejecuta 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. Luego se asigna un puntero de caracteres para apuntar al 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.

ninguna
fuente
6

La forma en C ++ ha sido usar boost , donde las comprobaciones y los moldes del preprocesador se dividen en compartimentos dentro de bibliotecas probadas a fondo.

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

La biblioteca endiana fue planeada para ser enviada al estándar C ++, y soporta una amplia variedad de operaciones en datos sensibles a .

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

fuzzyTew
fuente
1
Para su información, el enlace de "cuatro tipos diferentes de endianness" está roto,
Remy Lebeau
wiki fijo y realizado
fuzzyTew
5

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

En cuanto a encontrar endianness, haga lo siguiente:

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

Obtendrá tempChar para que sea 0x12 o 0x34, de donde conocerá la resistencia.

Samoz
fuente
3
Esto se basa en que short tiene exactamente 2 bytes, lo que no está garantizado.
Sharptooth
3
Sin embargo, sería una apuesta bastante segura basada en las dos arquitecturas dadas en la pregunta.
Daemin
8
Incluya stdint.hy use int16_tpara pruebas futuras contra cortocircuitos diferentes en otra plataforma.
Denise Skidmore
4

Haría algo como esto:

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

En este sentido, obtendría una función eficiente en el tiempo que solo realiza el cálculo una vez.

Jeremy Mayhew
fuente
puedes alinearlo? no estoy seguro si en línea causa múltiples bloques de memoria de las variables estáticas
aah134
4

Como se indicó anteriormente, use trucos de unión.

Sin embargo, existen pocos problemas con los que se aconsejaron anteriormente, sobre todo porque el acceso a la memoria no alineado es notoriamente lento para la mayoría de las arquitecturas, y algunos compiladores ni siquiera reconocerán dichos predicados constantes, a menos que las palabras estén alineadas.

Debido a que la simple prueba endian es aburrida, aquí va la función (plantilla) que cambiará la entrada / salida del entero arbitrario de acuerdo con sus especificaciones, independientemente de la arquitectura del host.

#include <stdint.h>

#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0

template <typename T>
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 dado a host, use:

host = endian(source, endian_of_source)

Para convertir de host endian a endian dado, use:

output = endian(hostsource, endian_you_want_to_output)

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

kat
fuente
4
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}
Paolo Brandoli
fuente
1
¿Sería esto equivalente? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Emanuel
4

¡No utilices a union!

C ++ no permite la escritura de tipos a través de unions!
¡Leer desde un campo sindical que no fue el último campo escrito es un comportamiento indefinido !
Muchos compiladores admiten hacerlo como extensiones, pero el lenguaje no garantiza.

Vea esta respuesta para más detalles:

https://stackoverflow.com/a/11996970


Solo hay dos respuestas válidas que están garantizadas para ser portátiles.

La primera respuesta, si tiene acceso a un sistema que admite C ++ 20,
es usarlo std::endiandesde el <type_traits>encabezado.

(Al momento de escribir, C ++ 20 aún no se ha lanzado, pero a menos que algo afecte std::endian la inclusión, esta será la forma preferida de probar la resistencia en tiempo de compilación desde C ++ 20 en adelante).

C ++ 20 en adelante

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

Antes de C ++ 20, la única respuesta válida es almacenar un número entero y luego inspeccionar su primer byte a través del tipo punteo.
A diferencia del uso de unions, esto está expresamente permitido por el sistema de tipos de C ++.

También es importante recordar que para una portabilidad óptima se static_castdebe usar,
porque la reinterpret_castimplementación está definida.

Si un programa intenta acceder al valor almacenado de un objeto a través de un valor gl diferente de uno de los siguientes tipos, el comportamiento es indefinido: ... a charo unsigned chartipo.

C ++ 11 en adelante

enum class endianness
{
    little = 0,
    big = 1,
};

inline endianness get_system_endianness()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
}

C ++ 11 en adelante (sin enumeración)

inline bool is_system_little_endian()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

C ++ 98 / C ++ 03

inline bool is_system_little_endian()
{
    const int value = 0x01;
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}
Pharap
fuente
3
union {
    int i;
    char c[sizeof(int)];
} x;
x.i = 1;
if(x.c[0] == 1)
    printf("little-endian\n");
else    printf("big-endian\n");

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

Neeraj
fuente
3

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
}
Hanshenrik
fuente
3

Declarar:

Mi publicación inicial se declara incorrectamente como "tiempo de compilación". No lo es, incluso es imposible en el estándar actual de C ++. Constexpr NO significa que la función siempre realiza cómputos en tiempo de compilación. Gracias Richard Hodges por la corrección.

Tiempo de compilación, no macro, solución C ++ 11 constexpr:

union {
  uint16_t s;
  unsigned char c[2];
} constexpr static  d {1};

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}
zhaorufei
fuente
2
¿Hay alguna razón particular por la que usaste unsigned char sobre uint8_t?
Kevin
0 tiempo de ejecución por encima ... me gusta!
hanshenrik
Supongo que esto detecta endiannes de la máquina de construcción, no el objetivo.
hutorny
2
¿No es esto UB en C ++?
rr-
66
Esto no es legal en el contexto constexpr. No puede acceder a un miembro de un sindicato que no se haya inicializado directamente. No hay forma de detectar legalmente el endianness en tiempo de compilación sin la magia del preprocesador.
Richard Hodges
2

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


fuente
1

A menos que el encabezado endian sea solo para 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!");
...
Mark A. Libby
fuente
No son éstos __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__y __ORDER_BIG_ENDIAN__?
Xeverous
1

Si no desea una compilación condicional, simplemente puede escribir código independiente endian. Aquí hay un ejemplo (tomado de Rob Pike ):

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

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

El mismo código, tratando de tener en cuenta la resistencia 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
fjardon
fuente
¡Qué buena idea! Y ahora transfiramos sus enteros a través de un enchufe de red a un dispositivo desconocido.
Maksym Ganenko el
@MaksymGanenko No recibo tu comentario. ¿Es ironía? Estoy no sugiriendo que no se especifique orden de bits de datos serializados. Sugiero no escribir código que dependa de la resistencia de la máquina que recibe los datos.
fjardon
@MaksymGanenko Si vota negativamente, podría explicar por qué la respuesta es incorrecta. Como mínimo para ayudar a los lectores potenciales a entender por qué no deberían seguir mi respuesta.
fjardon
0
int i=1;
char *c=(char*)&i;
bool littleendian=c;
Jon Bright
fuente
0

¿Qué tal esto?

#include <cstdio>

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;
}
Abhay
fuente
0

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

#include <limits.h>

#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 compilación.

Christoph
fuente
0

Los compiladores forma de C (al menos todos los que conozco de) trabajar el orden de bits ha de ser decidido en tiempo de compilación. Incluso para procesadores biendianos (como ARM och MIPS) debe elegir endianness en tiempo de compilación. Además, el endianness 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 tenga que hacerse en ensamblador.

Fabel
fuente
-1

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

Se ha observado que un ejecutable dado no debería ejecutarse en dos órdenes de bytes diferentes, pero no tengo idea de si ese es siempre el caso, y me parece un truco comprobar en el momento de la compilación. Entonces codifiqué esta función:

#include <stdint.h>

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, a pesar de que optimiza los otros códigos aquí. Creo que es porque dejo el valor "aleatorio" que estaba alojado en la memoria de bytes más pequeña como estaba (al menos 7 de sus bits), por lo que el compilador no puede saber cuál es ese valor aleatorio y no se 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.

Tex Killer
fuente
¿Por qué asignar 4 bytes para trabajar en un valor de 2 bytes? ¿Por qué enmascarar un valor indeterminado con 0x7FE? ¿Por qué usar malloc()en absoluto? Eso es un desperdicio. Y _BEes una pérdida de memoria (aunque pequeña) y una condición de carrera esperando a suceder, los beneficios de almacenar en caché el resultado dinámicamente no valen la pena. En cambio, haría algo más como esto: static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&teste)[1]); }simple y efectivo, y mucho menos trabajo para realizar en tiempo de ejecución.
Remy Lebeau
@RemyLebeau, el objetivo de mi respuesta fue producir un código que el compilador no haya optimizado. Claro, su código es mucho más simple, pero con las optimizaciones activadas, se convertirá en un booleano constante después de compilado. Como dije en mi respuesta, en realidad no sé si hay alguna forma de compilar el código C de manera que el mismo ejecutable se ejecute en ambas órdenes de bytes, y también tenía curiosidad por ver si podía hacer la verificación en tiempo de ejecución a pesar de las optimizaciones que están en marcha.
Tex Killer
@TexKiller, ¿por qué no simplemente deshabilitar las optimizaciones para el código? Usando volatile, o #pragma, etc.
Remy Lebeau
@RemyLebeau, no conocía esas palabras clave en ese momento, y simplemente lo tomé como un pequeño desafío para evitar la optimización del compilador con lo que sabía.
Tex Killer
-1

Si bien no hay una forma rápida y estándar de determinarlo, esto lo generará:

#include <stdio.h> 
int main()  
{ 
   unsigned int i = 1; 
   char *c = (char*)&i; 
   if (*c)     
       printf("Little endian"); 
   else
       printf("Big endian"); 
   getchar(); 
   return 0; 
} 
yekanchi
fuente
-1

Ver Endianness - C-Level Code illustration.

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANNESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANNESS 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;
} 
gimel
fuente
-2

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

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

#include <stdio.h>

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 bytes. Podríamos usar un puntero de caracteres para señalar el int con el valor 1. Por lo tanto, si la computadora es little endian, el carácter al que apunta el puntero de caracteres tiene el valor 1; de lo contrario, su valor debería ser 0.

Arquímedes520
fuente
esto se mejoraría usando int32t.
shuttle87
1
^ si quieres hacer una trampa, lo mejor aquí es int16_fast_t. y el código actual de @ Archimedes520 no funcionará en un arco donde int es nativamente int8;) (eso podría ir en contra de los estándares c en primer lugar, sin embargo)
hanshenrik