← Volver al blogArquitectura de CPU

Endianness: big-endian frente a little-endian (con diagramas de bus)

Big-endian coloca el byte más significativo en la dirección menor; little-endian lo invierte. Diagramas de bus, distribuciones de 0x12345678 y por qué x86 y TCP/IP difieren.

DennyDenny
11 min de lectura
Chalkboard feature illustration for “Endianness: big-endian frente a little-endian (con diagramas de bus)”

En resumen: Endianness es la convención que determina cómo se dispone en memoria un valor de varios bytes. Big-endian sitúa el byte más significativo en la dirección menor, como hacen numerosos campos de protocolos de red y muchos sistemas PowerPC históricos. Little-endian sitúa allí el byte menos significativo, como x86 y los perfiles estándar de RISC-V. Siempre que los bytes atraviesen una interfaz, deben comprobarse por separado la arquitectura, el modo de funcionamiento y el protocolo.

Un entero de 32 bits ocupa 4 bytes. La memoria se direcciona de un byte en un byte. Así que, al escribir un valor de 32 bits en memoria, ¿qué byte se coloca en la dirección más baja? Esa elección es el endianness. Es una de las pocas decisiones de la arquitectura de computadores en la que dos respuestas razonables persisten una junto a la otra y en la que equivocarse produce números erróneos sin avisar.

Qué significa «endianness»

Considera el valor de 32 bits 0x123456780\text{x}12345678. Tiene cuatro bytes:

  • 0x120\text{x}12 — byte más significativo (MSB)
  • 0x340\text{x}34
  • 0x560\text{x}56
  • 0x780\text{x}78 — byte menos significativo (LSB)

Para almacenarlo en memoria en la dirección AA, debes colocar cuatro bytes en las direcciones AA, A+1A+1, A+2A+2, A+3A+3. Hay dos opciones principales:

Big-endian. Almacena primero el MSB.

DirecciónAAA+1A+1A+2A+2A+3A+3
Byte0x120\text{x}120x340\text{x}340x560\text{x}560x780\text{x}78

Little-endian. Almacena primero el LSB.

DirecciónAAA+1A+1A+2A+2A+3A+3
Byte0x780\text{x}780x560\text{x}560x340\text{x}340x120\text{x}12

El mismo valor de 32 bits, los mismos cuatro bytes, distribuciones de memoria completamente distintas. Un programa que escribe 0x123456780\text{x}12345678 en una máquina big-endian y otro que lee cuatro bytes de AA..A+3A+3 en una máquina little-endian, byte a byte, reconstruirá 0x785634120\text{x}78563412. Mismos bytes, número equivocado.

Los nombres proceden de un ensayo de 1980 de Danny Cohen, quien tomó la idea de Los viajes de Gulliver: los liliputienses libraron una guerra por decidir qué extremo de un huevo cocido debían romper primero. El objetivo de la analogía era mostrar que la elección es arbitraria y, aun así, que los grupos se comprometen con ella lo suficiente como para iniciar una pelea.

Diagrama de bus de la lectura

La convención de endianness la impone la unidad de carga y almacenamiento de la CPU. Cuando la CPU ejecuta una carga de 32 bits desde la dirección AA, coloca AA en el bus de direcciones y toma 4 bytes del bus de datos.

   CPU                         Memory
+--------+   address=A     +---------+
|  load  | ───────────────>|  byte 0 | A    : 0x12 (big) or 0x78 (little)
| 32-bit |                 |  byte 1 | A+1  : 0x34 (big) or 0x56 (little)
|        |   data[31:0]    |  byte 2 | A+2  : 0x56 (big) or 0x34 (little)
|        | <───────────────|  byte 3 | A+3  : 0x78 (big) or 0x12 (little)
+--------+                 +---------+

En una CPU big-endian, el byte 0 (en la dirección más baja) se desplaza al canal más significativo del registro de 32 bits; el byte 3 llega al canal menos significativo. En una CPU little-endian, el byte 0 llega al canal menos significativo y el byte 3 al más significativo.

Las referencias de componentes REGISTER y RAM describen los elementos de almacenamiento que participan en esta transferencia. El endianness es estrictamente una convención de cableado entre ellos: qué byte del registro ancho se conecta a qué dirección de byte de memoria.

En el acceso síncrono a memoria, los bytes del bus suelen presentarse simultáneamente a través de una ruta de datos ancha; en un bus de 8 bits más estrecho, los mismos bytes llegan de forma secuencial en cuatro ciclos. La convención de endianness se aplica de manera idéntica en ambos casos.

Ejemplo práctico: 0x12345678 en C

Considera el siguiente fragmento de pseudocódigo:

uint32_t x = 0x12345678;
uint8_t  *p = (uint8_t *)&x;
printf("%02x %02x %02x %02x\n", p[0], p[1], p[2], p[3]);

En una máquina big-endian: 12 34 56 78. En una máquina little-endian: 78 56 34 12. El puntero pp recorre la memoria byte a byte y el orden de los bytes es exactamente la distribución en memoria, que cambia con el endianness.

Esta es la prueba estándar de endianness. También es, por cierto, una fuente habitual de errores: el código que usa trucos de conversión de punteros para «extraer» bytes individuales de un entero depende del endianness y solo funciona en un anfitrión sin intercambio explícito de bytes.

Qué arquitecturas eligen cada orden

ArquitecturaEndianness predeterminado
x86, x86-64Little-endian
RISC-VLittle-endian (existe una variante big-endian)
ARMBi-endian, configurable; little-endian es abrumadoramente común
PowerPCHistóricamente big-endian; los núcleos posteriores son bi-endian
MIPSBi-endian; los valores predeterminados varían según el SoC
SPARCBig-endian (V9 es bi-endian)
Motorola 68kBig-endian
z/Architecture (mainframe de IBM)Big-endian
Orden de bytes de red (cabeceras TCP/IP, IPv4, IPv6)Big-endian

La tendencia actual es claramente hacia little-endian. x86 y ARM juntos representan la inmensa mayoría de las CPU en funcionamiento. Los protocolos de red siguen siendo big-endian por razones históricas: TCP/IP se especificó a finales de la década de 1970, cuando los PDP de DEC big-endian y los mainframes de IBM todavía marcaban la pauta.

Por qué persisten dos grupos

Cada lado tiene ventajas reales, aunque ninguna es decisiva.

El argumento a favor de big-endian

La lectura de arriba abajo coincide con cómo escribimos los números. 0x120\text{x}12 es el primer byte que ves en memoria y también el primer dígito que escribirías en papel. Un volcado hexadecimal de una estructura big-endian se lee directamente.

Orden de bytes de red. Internet funciona en big-endian por especificación (RFC 791, RFC 1700). Siempre que un protocolo binario se serializa en un cable, big-endian es la convención establecida.

La comparación de signo es directa. El bit de signo de un entero en complemento a dos está en el byte más significativo; una máquina big-endian lo ve en la dirección más baja, por lo que una carga con extensión de signo solo tiene que examinar el primer byte obtenido.

El argumento a favor de little-endian

LSB en LSB. El byte en la dirección AA es el byte menos significativo del entero ubicado en AA. Esto significa que un puntero de 32 bits y otro de 8 bits a la misma dirección apuntan ambos al byte menos significativo. Convertir int* en char* no mueve la dirección, de modo que leer un byte bajo de un valor de 32 bits desde un registro de 16 bits es una operación gratuita.

Orden de acarreo aritmético. La suma de precisión múltiple comienza en el byte menos significativo y propaga los acarreos hacia arriba, es decir, hacia direcciones mayores. Una máquina little-endian lee operandos y escribe resultados en el orden natural de recorrido de la memoria. Este fue un argumento de rendimiento real en el 8086 de 1978, con su capacidad de búfer limitada.

Cargas independientes de la anchura. Un valor de 32 bits en la dirección AA equivale a un valor de 16 bits en AA seguido de otro valor de 16 bits en A+2A+2, y también a cuatro valores de 8 bits que comienzan en AA. La reinterpretación de tipos entre anchuras es invisible en el byte más bajo. Esto explica en parte por qué el software de x86 toleró históricamente conversiones indiscriminadas.

Ningún bando ha vencido. ARM es el compromiso arquitectónico: bi-endian, con el kernel eligiendo al arrancar.

Cuándo importa el endianness

En la programación cotidiana de una sola máquina, el endianness es invisible. La CPU ordena correctamente los bytes de sus propios tipos enteros, el compilador genera las cargas y los almacenamientos adecuados y el programa nunca tiene que pensarlo.

Se hace visible en cuanto lees o escribes bytes, no enteros, y esos bytes tienen que significar lo mismo en otro lugar.

Formatos de archivo binarios. Cabeceras ELF, fragmentos PNG, cajas MP4, GIF, BMP y ZIP. Algunos son big-endian (PNG), otros little-endian (BMP, ZIP) y unos pocos mezclan ambos. Leer una cabecera ELF en un anfitrión con el orden incorrecto sin intercambiar bytes devuelve desplazamientos sin sentido.

Protocolos de red. TCP, UDP, IP, ICMP, DNS y NTP son todos big-endian. Las funciones de la biblioteca C htonl, htons, ntohl y ntohs («host a network long/short» y sus inversas) existen precisamente para convertir entre el orden de bytes del anfitrión y el orden de bytes de red. En un anfitrión big-endian no hacen nada; en uno little-endian intercambian bytes.

#include <arpa/inet.h>

uint32_t host_value = 0x12345678;
uint32_t net_value  = htonl(host_value);  // 0x78563412 on x86
                                          // 0x12345678 on a big-endian host

Los bytes en el cable son idénticos en ambos anfitriones. La representación en memoria es distinta.

Protocolos de dispositivos. La memoria flash SPI, las EEPROM I2C y los registros de sensores suelen elegir un orden de bytes en su hoja de datos y exigen que el anfitrión lo respete. Muchos son big-endian; algunos son mixtos.

Reinterpretación de tipos. Convertir, mediante un puntero o una unión, entre tipos enteros de distinta anchura y esperar bytes en desplazamientos fijos es un patrón que «simplemente funciona» en un anfitrión y falla silenciosamente en el otro.

Registros mapeados en memoria en sistemas de endianness mixto. Una CPU big-endian que controla un periférico little-endian (o viceversa) necesita instrucciones explícitas de intercambio de bytes en cada acceso a registro.

La regla general es: en cuanto los bytes salen de la custodia exclusiva de la CPU, el endianness se convierte en un contrato que debe cumplirse. Dentro de un proceso en un anfitrión, puedes ignorarlo. Entre máquinas, archivos, protocolos o periféricos, no puedes.

Endianness mixto y middle-endian

Una nota al margen en la historia del orden de bytes: algunos sistemas históricos usaban ordenamientos middle-endian, en los que los valores de 32 bits se almacenaban como dos mitades de 16 bits en orden big-endian, pero cada mitad era little-endian internamente. El PDP-11 utilizaba esta distribución para valores de 32 bits en algunos modos de funcionamiento. El resultado es que 0x123456780\text{x}12345678 almacenado como cuatro bytes se convierte en 0x34,0x12,0x78,0x560\text{x}34, 0\text{x}12, 0\text{x}78, 0\text{x}56: ni big-endian puro ni little-endian puro. Los sistemas modernos no usan middle-endian, pero aparece ocasionalmente en formatos de archivos binarios antiguos generados por herramientas de la época del PDP.

Algunos sistemas Arm también distinguían entre el BE-32 heredado y el BE-8 invariable por byte. La lección importante no es inferir la distribución de un periférico o un archivo a partir del nombre de la CPU: usa la documentación de arquitectura, bus y dispositivo para el acceso exacto.

Endianness en el ciclo de búsqueda-decodificación-ejecución

Cuando una CPU ejecuta una instrucción, tiene que obtener de memoria la palabra de instrucción. En una CPU big-endian, la palabra de instrucción se dispone con el byte superior primero; en una CPU little-endian, con el byte inferior primero. La lógica de decodificación debe coincidir. Esto es invisible durante el funcionamiento normal: la CPU solo decodifica su propio flujo de instrucciones, de modo que su endianness es coherente; pero importa al leer un binario entre anfitriones, por ejemplo, para un depurador o desensamblador que examina un binario compilado para otro destino.

La canalización completa de obtención de instrucciones se explica en la entrada sobre el ciclo de búsqueda-decodificación-ejecución; el endianness es el último detalle que debe alinearse entre la memoria y el contador de programa.

Por qué ningún orden es un ganador universal del hardware

Mirar un volcado hexadecimal de 0x123456780\text{x}12345678 en x86, 78 56 34 12, resulta extraño. El ojo lee de izquierda a derecha, los dígitos están invertidos por pares y la conversión mental es incómoda.

Little-endian sí ofrece una propiedad práctica: la dirección de un entero de varios bytes también es la dirección de su byte menos significativo. Una suma de precisión múltiple, byte a byte, puede avanzar hacia direcciones crecientes mientras propaga el acarreo. Ampliar in situ un entero little-endian también conserva la dirección de su parte de orden bajo.

Big-endian ofrece la propiedad complementaria: los bytes aparecen en orden de significancia, por lo que un volcado de memoria o una comparación lexicográfica comienza por la parte más significativa. El orden de bytes de red usa esta convención de octeto más significativo primero para valores de varios octetos.

Los procesadores modernos normalmente cargan una palabra completa y reordenan los bytes en la ruta de carga y almacenamiento según sea necesario. El endianness sigue siendo crucial en los límites de la memoria y las interfaces, pero no cambia el resultado booleano de una suma con propagación de acarreo dentro de un registro. La adopción histórica refleja decisiones de arquitectura y compatibilidad, no un único circuito universalmente superior.

Detectar el endianness en tiempo de ejecución

Una pequeña comprobación en tiempo de ejecución en C puede inspeccionar la representación de un objeto mediante un puntero unsigned char, que C permite usar como alias de cualquier objeto:

int is_little_endian(void) {
    unsigned int x = 1;
    return *(const unsigned char *)&x == 1;
}

Si el byte en la dirección más baja es 1, el byte menos significativo está primero. Un resultado falso no demuestra por sí solo un orden big-endian convencional en una implementación exótica de endianness mixto.

No atribuyas <endian.h>, <sys/endian.h> ni macros del compilador como __BYTE_ORDER__ a C99: son recursos de la plataforma o del compilador. C23 añadió identificadores de endianness nativo mediante <stdbit.h>, pero el soporte disponible de los compiladores y los modos de lenguaje de los proyectos varían. Para una serialización portátil, el diseño más sólido consiste en definir explícitamente los bytes en el cable y ensamblar o desensamblar enteros con desplazamientos, máscaras o una rutina de biblioteca documentada como htonl/ntohl.

Para simularlo en DigiSim, puedes construir un pequeño bucle de lectura-modificación-escritura que cargue un byte en una dirección baja, lo coloque en el byte alto de un registro y verifique el resultado frente a un patrón esperado: una manera práctica de percibir cómo la diferencia de cableado afecta al valor. La entrada sobre el viaje de 3.500 años del binario sitúa esto en el contexto más amplio de cómo el orden de dígitos legible para humanos interactúa con la conveniencia de las máquinas.

Qué sigue

El endianness es una de las convenciones de nivel de byte necesarias para intercambiar datos binarios. Las otras incluyen la propia codificación de enteros (aritmética con signo en complemento a dos) y el formato de coma flotante (IEEE 754). La explicación detallada de búsqueda–decodificación–ejecución conecta esas representaciones con el procesamiento de instrucciones.

Abre la plantilla básica de sistema de memoria RAM para ver la memoria direccionada por bytes en acción; después escribe un valor de 32 bits en cuatro direcciones y léelo de nuevo con un orden de bytes diferente para experimentar directamente la diferencia de cableado.

Referencias principales

Convierte la idea en un circuito funcional.