little - Tipos de endianness
little endian bits (8)
También hay endian medio o mixto. Ver wikipedia para más detalles.
La única vez que tuve que preocuparme por esto fue cuando escribí un código de red en C. El uso de redes usualmente usa Big-endian IIRC. La mayoría de los lenguajes lo resumen o ofrecen bibliotecas para garantizar que estás usando el endian-derecho correcto.
¿Cuál es la diferencia entre los siguientes tipos de endianness?
- byte (8b) invariante grande y pequeño endianness
- media palabra (16b) invariante grande y poco endianness
- palabra (32b) invariante grande y poco endianness
- doble palabra (64b) invariante grande y poco endianness
¿Hay otros tipos / variaciones?
el concepto básico es el orden de los bits:
1010 0011
en little-endian es lo mismo que
0011 1010
en big-endian (y viceversa).
Notará que el orden cambia por agrupación, no por bit individual. No sé de un sistema, por ejemplo, donde
1100 0101
sería la versión "other-endian" de la primera versión.
El mejor artículo que leí sobre endianness es " Entender el orden de bytes Big y Little Endian ".
En realidad, describiría el endianismo de una máquina como el orden de los bytes dentro de una palabra, y no el orden de los bits .
Por "bytes" allá me refiero a la "unidad de memoria más pequeña que la arquitectura puede gestionar individualmente". Por lo tanto, si la unidad más pequeña tiene 16 bits de longitud (lo que en x86 se llamaría una palabra ), una "palabra" de 32 bits que representa el valor 0xFFFF0000 podría almacenarse así:
FFFF 0000
o esto:
0000 FFFF
en memoria, dependiendo de endianness.
Por lo tanto, si tiene endianidad de 8 bits, significa que cada palabra que consta de 16 bits se almacenará como:
FF 00
o:
00 FF
y así.
Hay dos enfoques para el mapeo endian: invariancia de direcciones e invariancia de datos .
Dirección Invariancia
En este tipo de mapeo, la dirección de los bytes se conserva siempre entre grande y pequeño. Esto tiene el efecto secundario de invertir el orden de importancia (más significativo a menos significativo) de un dato particular (por ejemplo, palabra de 2 o 4 bytes) y, por lo tanto, la interpretación de los datos. Específicamente, en little-endian, la interpretación de datos es menos significativa para los bytes más significativos, mientras que en big-endian, la interpretación es de lo más significativo a lo menos significativo. En ambos casos, el conjunto de bytes a los que se accede sigue siendo el mismo.
Ejemplo
Invarianza de dirección (también conocida como invarianza de bytes ): la dirección de byte es constante pero la importancia de bytes se invierte.
Addr Memory
7 0
| | (LE) (BE)
|----|
+0 | aa | lsb msb
|----|
+1 | bb | : :
|----|
+2 | cc | : :
|----|
+3 | dd | msb lsb
|----|
| |
At Addr=0: Little-endian Big-endian
Read 1 byte: 0xaa 0xaa (preserved)
Read 2 bytes: 0xbbaa 0xaabb
Read 4 bytes: 0xddccbbaa 0xaabbccdd
Invarianza de datos
En este tipo de mapeo, la importancia del byte relativo se conserva para el dato de un tamaño particular. Por lo tanto, existen diferentes tipos de asignaciones endian invariantes de datos para diferentes tamaños de dato. Por ejemplo, un mapeo de endian invariante de palabra de 32 bits se usaría para un tamaño de dato de 32. El efecto de preservar el valor de dato de tamaño particular es que las direcciones de byte de bytes dentro del dato se invierten entre mapeos de endian grande y pequeño .
Ejemplo
Invarianza de datos de 32 bits (también conocida como invarianza de palabras ): el datum es una palabra de 32 bits que siempre tiene el valor 0xddccbbaa
, independientemente de la endianidad. Sin embargo, para accesos menores a una palabra, la dirección de los bytes se invierte entre grandes y pequeñas asignaciones endian.
Addr Memory
| +3 +2 +1 +0 | <- LE
|-------------------|
+0 msb | dd | cc | bb | aa | lsb
|-------------------|
+4 msb | 99 | 88 | 77 | 66 | lsb
|-------------------|
BE -> | +0 +1 +2 +3 |
At Addr=0: Little-endian Big-endian
Read 1 byte: 0xaa 0xdd
Read 2 bytes: 0xbbaa 0xddcc
Read 4 bytes: 0xddccbbaa 0xddccbbaa (preserved)
Read 8 bytes: 0x99887766ddccbbaa 0x99887766ddccbbaa (preserved)
Ejemplo
Invarianza de datos de 16 bits (también conocida como invarianza de media palabra ): el dato es un 16 bits que siempre tiene el valor 0xbbaa
, independientemente de la endianidad. Sin embargo, para accesos de menos de media palabra, la dirección de los bytes se invierte entre grandes y pequeñas asignaciones endian.
Addr Memory
| +1 +0 | <- LE
|---------|
+0 msb | bb | aa | lsb
|---------|
+2 msb | dd | cc | lsb
|---------|
+4 msb | 77 | 66 | lsb
|---------|
+6 msb | 99 | 88 | lsb
|---------|
BE -> | +0 +1 |
At Addr=0: Little-endian Big-endian
Read 1 byte: 0xaa 0xbb
Read 2 bytes: 0xbbaa 0xbbaa (preserved)
Read 4 bytes: 0xddccbbaa 0xddccbbaa (preserved)
Read 8 bytes: 0x99887766ddccbbaa 0x99887766ddccbbaa (preserved)
Ejemplo
Invarianza de datos de 64 bits (también conocida como invarianza de palabras dobles ): el datum es una palabra de 64 bits que siempre tiene el valor 0x99887766ddccbbaa
, independientemente de la endianidad. Sin embargo, para accesos más pequeños que una palabra doble, la dirección de los bytes se invierte entre grandes y pequeñas asignaciones endian.
Addr Memory
| +7 +6 +5 +4 +3 +2 +1 +0 | <- LE
|---------------------------------------|
+0 msb | 99 | 88 | 77 | 66 | dd | cc | bb | aa | lsb
|---------------------------------------|
BE -> | +0 +1 +2 +3 +4 +5 +6 +7 |
At Addr=0: Little-endian Big-endian
Read 1 byte: 0xaa 0x99
Read 2 bytes: 0xbbaa 0x9988
Read 4 bytes: 0xddccbbaa 0x99887766
Read 8 bytes: 0x99887766ddccbbaa 0x99887766ddccbbaa (preserved)
En términos prácticos, endianess se refiere a la forma en que el procesador interpretará el contenido de una ubicación de memoria determinada. Por ejemplo, si tenemos la ubicación de memoria 0x100 con el siguiente contenido (bytes hexadecimales)
0x100: 12 34 56 78 90 ab cd ef
Reads Little Endian Big Endian
8-bit: 12 12
16-bit: 34 12 12 34
32-bit: 78 56 34 12 12 34 56 78
64-bit: ef cd ab 90 78 56 34 12 12 34 56 78 90 ab cd ef
Las dos situaciones en las que debes tener en cuenta la endialeza son con el código de red y si lo haces con los punteros.
TCP / IP especifica que los datos en el cable deben ser big endian. Si transmite tipos que no sean matrices de bytes (como punteros a estructuras), debe asegurarse de usar las macros ntoh / hton para garantizar que los datos se envíen a big endian. Si envía desde un procesador little-endian a un procesador big-endian (o viceversa), los datos se distorsionarán ...
Casting issues:
uint32_t* lptr = 0x100;
uint16_t data;
*lptr = 0x0000FFFF
data = *((uint16_t*)lptr);
¿Cuál será el valor de los datos? En un sistema big-endian, sería 0 en un sistema little-endian, sería FFFF
Hace 13 años trabajé en una herramienta portátil tanto para un sistema DEC ALPHA como para una PC. En este DEC ALFA, los bits en realidad estaban invertidos . Es decir:
1010 0011
en realidad traducido a
1100 0101
Era casi transparente y sin fisuras en el código C, excepto que tenía un bitfield declarado como
typedef struct {
int firstbit:1;
int middlebits:10;
int lastbits:21;
};
que necesitaba ser traducido a (usando la compilación condicional #ifdef)
typedef struct {
int lastbits:21;
int middlebits:10;
int firstbit:1;
};
Philibert dijo:
bits fueron invertidos
Dudo que cualquier arquitectura rompa la invarianza de valores de bytes. El orden de los campos de bits puede necesitar inversión al mapear estructuras que los contienen contra datos. Tal mapeo directo se basa en los detalles del compilador que están fuera del estándar C99 pero que aún pueden ser comunes. El mapeo directo es más rápido pero no cumple con el estándar C99 que no estipula el empaque, la alineación y el orden de bytes. El código que cumple con C99 debe usar un mapeo lento basado en valores en lugar de direcciones. Es decir, en lugar de hacer esto,
#if LITTLE_ENDIAN
struct breakdown_t {
int least_significant_bit: 1;
int middle_bits: 10;
int most_significant_bits: 21;
};
#elif BIG_ENDIAN
struct breakdown_t {
int most_significant_bits: 21;
int middle_bits: 10;
int least_significant_bit: 1;
};
#else
#error Huh
#endif
uint32_t data = ...;
struct breakdown_t *b = (struct breakdown_t *)&data;
uno debería escribir esto (y así es como el compilador generaría código de todos modos, incluso para el "mapeo directo" anterior),
uint32_t data = ...;
uint32_t least_significant_bit = data & 0x00000001;
uint32_t middle_bits = (data >> 1) & 0x000003FF;
uint32_t most_significant_bits = (data >> 11) & 0x001fffff;
La razón detrás de la necesidad de invertir el orden de los campos de bits en cada unidad de almacenamiento de datos específica de aplicación endian-neutral es que los compiladores empaquetan campos de bits en bytes de direcciones crecientes.
El "orden de los bits" en cada byte no importa ya que la única forma de extraerlos es aplicando máscaras de valores y cambiando a la dirección del bit menos significativo o del bit más significativo. La cuestión del "orden de bits" solo sería importante en las arquitecturas imaginarias con la noción de direcciones de bit. Creo que todas las arquitecturas existentes ocultan esta noción en hardware y proporcionan solo la extracción de bit menos significativa frente a la más significativa, que es la noción basada en los valores de byte neutro endian.