Endianness: Big vs. Little (mit Busdiagrammen)
Big-Endian legt das höchstwertige Byte an die niedrigste Adresse; Little-Endian dreht es um. Busdiagramme, 0x12345678-Layouts und warum sich x86 und TCP/IP unterscheiden.
Kurz gesagt: Endianness legt fest, wie ein Mehrbytewert im Speicher angeordnet wird. Big-Endian speichert das höchstwertige Byte an der niedrigsten Adresse; so verfahren viele Felder in Netzwerkprotokollen und historisch viele PowerPC-Systeme. Little-Endian speichert dort das niederwertigste Byte, wie bei x86 und den Standardprofilen von RISC-V. Sobald Bytes eine Schnittstelle passieren, müssen Architektur, Betriebsart und Protokoll einzeln geprüft werden.
Eine 32-Bit-Ganzzahl benötigt 4 Bytes. Der Speicher wird jeweils byteweise adressiert. Wenn Sie also einen 32-Bit-Wert in den Speicher schreiben, welches Byte kommt an die niedrigste Adresse? Diese Entscheidung ist Endianness. Sie gehört zu den wenigen Entscheidungen in der Computerarchitektur, bei denen zwei vernünftige Antworten nebeneinander bestehen und ein Fehler stillschweigend falsche Zahlen erzeugt.
Was „Endianness“ bedeutet
Nehmen wir den 32-Bit-Wert . Er besteht aus vier Bytes:
- – höchstwertiges Byte (MSB)
- – niederwertigstes Byte (LSB)
Um diesen Wert ab Adresse zu speichern, müssen Sie vier Bytes an den Adressen , , und ablegen. Die beiden wichtigsten Möglichkeiten sind:
Big-Endian. Das MSB zuerst speichern.
| Adresse | ||||
|---|---|---|---|---|
| Byte |
Little-Endian. Das LSB zuerst speichern.
| Adresse | ||||
|---|---|---|---|---|
| Byte |
Derselbe 32-Bit-Wert, dieselben vier Bytes, völlig unterschiedliche Speicherlayouts. Ein Programm, das auf einer Big-Endian-Maschine schreibt, und ein Programm, das vier Bytes ab bis auf einer Little-Endian-Maschine byteweise liest, rekonstruieren . Dieselben Bytes, die falsche Zahl.
Die Namen gehen auf einen Essay von Danny Cohen aus dem Jahr 1980 zurück, der sich von Gullivers Reisen inspirieren ließ: Die Liliputaner führten Krieg darüber, an welchem Ende eines gekochten Eis man zuerst die Schale aufbrechen sollte. Die Analogie sollte zeigen, dass die Wahl willkürlich ist und Gruppen sich dennoch so entschieden darauf festlegen, dass sie darüber einen Streit beginnen.
Ein Busdiagramm des Lesens
Die Endianness-Konvention wird von der Load/Store-Einheit der CPU umgesetzt. Wenn die CPU einen 32-Bit-Load ab Adresse ausführt, legt sie auf den Adressbus und nimmt 4 Bytes vom Datenbus.
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)
+--------+ +---------+
Auf einer Big-Endian-CPU wird Byte 0 (an der niedrigsten Adresse) in die höchstwertige Spur des 32-Bit-Registers verschoben; Byte 3 landet in der niederwertigsten Spur. Auf einer Little-Endian-CPU landet Byte 0 in der niederwertigsten Spur und Byte 3 in der höchstwertigsten.
Die Referenzen zu den Komponenten REGISTER und RAM beschreiben die Speicherprimitive, die an diesem Transfer beteiligt sind. Endianness ist lediglich eine Verdrahtungskonvention zwischen ihnen: Welches Byte des breiten Registers mit welcher Byteadresse des Speichers verbunden ist.
Bei einem synchronen Speicherzugriff werden die Bytes auf dem Bus typischerweise gleichzeitig über einen breiten Datenpfad präsentiert; bei einem schmaleren 8-Bit-Bus kommen dieselben Bytes nacheinander in vier Zyklen an. Die Endianness-Konvention gilt in beiden Fällen identisch.
Durchgerechnetes Beispiel: 0x12345678 in C
Betrachten Sie das folgende Pseudocode-Snippet:
uint32_t x = 0x12345678;
uint8_t *p = (uint8_t *)&x;
printf("%02x %02x %02x %02x\n", p[0], p[1], p[2], p[3]);
Auf einer Big-Endian-Maschine: 12 34 56 78. Auf einer Little-Endian-Maschine: 78 56 34 12. Der Zeiger bewegt sich Byte für Byte durch den Speicher, und die Byte-Reihenfolge entspricht exakt dem Layout im Speicher – das sich je nach Endianness unterscheidet.
Dies ist die Standardprüfung auf Endianness. Sie ist nebenbei auch eine häufige Fehlerquelle: Code, der Pointer-Cast-Tricks verwendet, um einzelne Bytes aus einer Ganzzahl „herauszulösen“, hängt von der Endianness ab und funktioniert ohne expliziten Byte-Tausch nur auf einem Host.
Welche Architekturen wählen was?
| Architektur | Standard-Endianess |
|---|---|
| x86, x86-64 | Little-Endian |
| RISC-V | Little-Endian (Big-Endian-Variante existiert) |
| ARM | Bi-Endian, konfigurierbar; Little-Endian ist mit großem Abstand üblich |
| PowerPC | Historisch Big-Endian; spätere Kerne sind bi-endian |
| MIPS | Bi-Endian; Standard hängt vom SoC ab |
| SPARC | Big-Endian (V9 ist bi-endian) |
| Motorola 68k | Big-Endian |
| z/Architecture (IBM-Mainframe) | Big-Endian |
| Netzwerk-Byte-Reihenfolge (TCP/IP-, IPv4-, IPv6-Header) | Big-Endian |
Der Trend geht heute eindeutig zu Little-Endian. x86 und ARM machen zusammen den überwältigenden Anteil der eingesetzten CPUs aus. Netzwerkprotokolle bleiben aus historischen Gründen Big-Endian – TCP/IP wurde in den späten 1970er-Jahren spezifiziert, als Big-Endian-DEC-PDPs und IBM-Mainframes noch den Ton angaben.
Warum zwei Lager fortbestehen
Beide Seiten haben echte Vorteile, aber keiner ist entscheidend.
Das Argument für Big-Endian
Von oben nach unten lesen entspricht der menschlichen Schreibweise. ist das erste Byte, das Sie im Speicher sehen, und zugleich die erste Ziffer, die Sie auf Papier schreiben würden. Ein Hexdump einer Big-Endian-Struktur ist unmittelbar lesbar.
Netzwerk-Byte-Reihenfolge. Das Internet verwendet laut Spezifikation Big-Endian (RFC 791, RFC 1700). Wo ein Binärprotokoll auf eine Leitung serialisiert wird, ist Big-Endian die etablierte Konvention.
Vorzeichenvergleich direkt. Das Vorzeichenbit einer vorzeichenbehafteten Ganzzahl im Zweierkomplement liegt im höchstwertigen Byte; eine Big-Endian-Maschine sieht es an der niedrigsten Adresse, sodass ein vorzeichenerweiternder Load nur das zuerst geladene Byte betrachten muss.
Das Argument für Little-Endian
LSB am LSB. Das Byte an Adresse ist das niederwertigste Byte der Ganzzahl an Adresse . Das bedeutet, dass ein 32-Bit-Zeiger und ein 8-Bit-Zeiger auf dieselbe Adresse beide auf das niederwertigste Byte zeigen. Ein Cast von int* zu char* verändert die Adresse nicht; das Lesen des niederwertigen Bytes einer 32-Bit-Zahl aus einem 16-Bit-Register ist somit kostenlos.
Reihenfolge des arithmetischen Übertrags. Mehrpräzisionsaddition beginnt beim niederwertigsten Byte und lässt Überträge nach oben – zu höheren Adressen – weiterlaufen. Eine Little-Endian-Maschine liest Operanden und schreibt Ergebnisse in der natürlichen Reihenfolge des Speicherzugriffs. Das war 1978 beim 8086 mit begrenzter Pufferung ein echtes Leistungsargument.
Breitenunabhängige Loads. Ein 32-Bit-Wert an Adresse ist dasselbe wie ein 16-Bit-Wert an Adresse , gefolgt von einem weiteren 16-Bit-Wert an , und dasselbe wie vier 8-Bit-Werte ab . Das Umdeuten von Typen zwischen Breiten ist am niederwertigsten Byte unsichtbar. Das ist ein Grund, warum x86-Software historisch gewagte Casts tolerierte.
Keines der beiden Lager hat gewonnen. ARM ist der architektonische Kompromiss: bi-endian, wobei der Kernel beim Start auswählt.
Wann Endianness wichtig wird
Beim täglichen Programmieren auf einer einzelnen Maschine bleibt Endianness unsichtbar. Die CPU erledigt die Byte-Reihenfolge für ihre eigenen Ganzzahltypen korrekt, der Compiler erzeugt die richtigen Loads und Stores, und das Programm muss nie darüber nachdenken.
Sichtbar wird sie in dem Moment, in dem Sie Bytes – nicht Ganzzahlen – lesen oder schreiben und diese Bytes irgendwo anders dasselbe bedeuten müssen.
Binärdateiformate. ELF-Header, PNG-Chunks, MP4-Boxen, GIF, BMP, ZIP. Einige sind Big-Endian (PNG), einige Little-Endian (BMP, ZIP), und einige mischen beide. Einen ELF-Header auf einem Host mit falscher Endianness ohne Byte-Tausch zu lesen, liefert wieder nur unbrauchbare Offsets.
Netzwerkprotokolle. TCP, UDP, IP, ICMP, DNS, NTP – alle sind Big-Endian. Die C-Bibliotheksfunktionen htonl, htons, ntohl, ntohs („host to network long/short“ und die Umkehrungen) existieren ausdrücklich, um zwischen Host- und Netzwerk-Byte-Reihenfolge umzuwandeln. Auf einem Big-Endian-Host sind sie No-ops; auf einem Little-Endian-Host tauschen sie 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
Die Bytes auf der Leitung sind auf beiden Hosts identisch. Die Darstellung im Speicher unterscheidet sich.
Geräteprotokolle. SPI-Flash, I2C-EEPROMs und Sensorregister – die meisten legen in ihrem Datenblatt eine Byte-Reihenfolge fest und verlangen, dass der Host sie einhält. Viele sind Big-Endian; einige sind gemischt.
Typumdeutung. Das Casten zwischen Ganzzahltypen unterschiedlicher Breite über einen Pointer oder eine Union, in der Erwartung fester Byte-Offsets – ein Muster, das auf einem Host „einfach funktioniert“ und auf dem anderen stillschweigend scheitert.
Memory-Mapped-Register in gemischten Endian-Systemen. Eine Big-Endian-CPU, die ein Little-Endian-Peripheriegerät steuert (oder umgekehrt), benötigt bei jedem Registerzugriff explizite Byte-Tauschbefehle.
Die allgemeine Regel lautet: Sobald Bytes die exklusive Obhut der CPU verlassen, wird Endianness zu einem Vertrag, der durchgesetzt werden muss. Innerhalb eines Prozesses auf einem Host können Sie sie ignorieren. Über Maschinen-, Datei-, Protokoll- oder Peripheriegrenzen hinweg nicht.
Mixed-Endian und Middle-Endian
Eine Fußnote in der Geschichte der Byte-Reihenfolge: Einige historische Systeme verwendeten Middle-Endian-Anordnungen, bei denen 32-Bit-Werte als zwei 16-Bit-Hälften in Big-Endian-Reihenfolge gespeichert wurden, jede Hälfte intern jedoch Little-Endian war. Der PDP-11 verwendete dieses Layout für 32-Bit-Werte in einigen Betriebsarten. Dadurch wird der als vier Bytes gespeicherte Wert zu – weder reines Big- noch reines Little-Endian. Moderne Systeme verwenden Middle-Endian nicht, doch in alten Binärdateiformaten aus PDP-Zeit und -Werkzeugen taucht es gelegentlich auf.
Einige Arm-Systeme unterschieden außerdem zwischen dem historischen BE-32 und dem byteinvarianten BE-8. Die wichtige Lehre ist, das Layout eines Peripheriegeräts oder einer Datei nicht aus dem CPU-Namen abzuleiten: Verwenden Sie Architektur-, Bus- und Gerätedokumentation für den exakten Zugriff.
Endianness im Fetch-Decode-Execute-Zyklus
Wenn eine CPU einen Befehl ausführt, muss sie das Befehlswort aus dem Speicher laden. Auf einer Big-Endian-CPU liegt das Befehlswort Byte für Byte vom höchsten zum niedrigsten Wert; auf einer Little-Endian-CPU in umgekehrter Reihenfolge. Die Decodierlogik muss dazu passen. Im Normalbetrieb bleibt das unsichtbar – die CPU decodiert nur ihren eigenen Befehlsstrom, dessen Endianness konsistent ist –, aber beim Lesen einer Binärdatei auf einem anderen Host ist es wichtig, etwa wenn ein Debugger oder Disassembler ein für ein anderes Ziel gebautes Binary untersucht.
Die vollständige Pipeline des Befehlsabrufs wird im Artikel zum Fetch-Decode-Execute-Zyklus behandelt; Endianness ist das letzte Detail, das zwischen Speicher und Programmzähler zusammenpassen muss.
Warum keine Reihenfolge universell überlegen ist
Einen Hexdump von auf x86 – 78 56 34 12 – zu betrachten, fühlt sich verkehrt an. Das Auge liest von links nach rechts, die Ziffern stehen in umgekehrten Paaren und die gedankliche Umwandlung ist umständlich.
Little-Endian hat eine praktische Eigenschaft: Die Adresse einer Mehrbyte-Ganzzahl ist auch die Adresse ihres niederwertigsten Bytes. Eine byteweise Mehrpräzisionsaddition kann zu steigenden Adressen laufen und dabei den Übertrag weitergeben. Das Verbreitern einer Little-Endian-Ganzzahl an Ort und Stelle erhält außerdem die Adresse ihres niederwertigen Teils.
Big-Endian besitzt die ergänzende Eigenschaft: Bytes erscheinen in der Bedeutungsreihenfolge, sodass ein Speicherdump oder lexikografischer Vergleich mit dem höchstwertigen Teil beginnt. Die Netzwerk-Byte-Reihenfolge verwendet diese Konvention „höchstwertiges Oktett zuerst“ für Mehr-Oktett-Werte.
Moderne Prozessoren laden normalerweise ein vollständiges Wort und ordnen die Bytes im Load/Store-Pfad bei Bedarf um. Endianness bleibt an Speicher- und Schnittstellengrenzen entscheidend, verändert aber nicht das Boolesche Ergebnis einer im Register ausgeführten Ripple-Carry-Addition. Die historische Verbreitung spiegelt Architektur- und Kompatibilitätsentscheidungen wider, nicht eine einzelne universell überlegene Schaltung.
Endianness zur Laufzeit erkennen
Eine kleine Laufzeitprüfung in C kann die Repräsentation eines Objekts über einen unsigned char-Pointer untersuchen, den C auf jedes Objekt zeigen lassen darf:
int is_little_endian(void) {
unsigned int x = 1;
return *(const unsigned char *)&x == 1;
}
Wenn das Byte an der niedrigsten Adresse 1 ist, kommt das niederwertigste Byte zuerst. Ein falsches Ergebnis beweist bei einer exotischen Middle-Endian-Implementierung nicht allein eine gewöhnliche Big-Endian-Reihenfolge.
Ordnen Sie <endian.h>, <sys/endian.h> oder Compiler-Makros wie __BYTE_ORDER__ nicht C99 zu: Das sind Plattform- oder Compilerfunktionen. C23 fügte über <stdbit.h> native Endian-Bezeichner hinzu, doch Unterstützung in verfügbaren Compilern und Sprachmodi von Projekten ist unterschiedlich. Für portable Serialisierung ist es robuster, die Bytes auf der Leitung ausdrücklich zu definieren und Ganzzahlen mit Shifts, Masken oder einer dokumentierten Bibliotheksroutine wie htonl/ntohl zusammen- bzw. auseinanderzusetzen.
Für eine Simulation in DigiSim können Sie eine kleine Read-Modify-Write-Schleife bauen, die ein Byte an einer niedrigen Adresse lädt, es im hohen Byte eines Registers platziert und das Ergebnis mit einem erwarteten Muster vergleicht – eine praktische Möglichkeit, zu spüren, wie der Verdrahtungsunterschied den Wert beeinflusst. Der Artikel über die 3.500-jährige Geschichte der Binärzahl stellt dies in den größeren Zusammenhang, wie die für Menschen lesbare Ziffernreihenfolge mit der Bequemlichkeit von Maschinen zusammenspielt.
Wie es weitergeht
Endianness ist eine der Konventionen auf Byteebene, die zum Austausch von Binärdaten erforderlich sind. Zu den weiteren gehören die Ganzzahldarstellung selbst (vorzeichenbehaftete Arithmetik im Zweierkomplement) und das Gleitkommaformat (IEEE 754). Der Deep Dive zu Fetch–Decode–Execute verbindet diese Darstellungen mit der Befehlsverarbeitung.
Öffnen Sie die Vorlage für ein einfaches RAM-Speichersystem, um byteadressierten Speicher in Aktion zu sehen. Schreiben Sie anschließend einen 32-Bit-Wert über vier Adressen und lesen Sie ihn mit einer anderen Byte-Reihenfolge zurück, um den Verdrahtungsunterschied direkt zu spüren.



