Warum sizeof(Struktur) nicht gleich der Summe sizeof(der Felder) ist? Vollständige Analyse

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Wie ist das möglich? Einführung in das sizeof-Paradoxon

\n\n

Stellen Sie sich vor: Sie erstellen eine einfache Struktur in C++ mit zwei Feldern — char (1 Byte) und int (4 Bytes). Logischerweise würden Sie annehmen, dass ihre Größe 5 Bytes beträgt. Sie schreiben Code, rufen sizeof auf und... erhalten 8 Bytes. "Unsinn!", denken Sie. "Ist der Compiler verrückt geworden?"

\n\n

Diese Frage ist eine der beliebtesten auf Stack Overflow (über 200.000 Aufrufe). Und es geht nicht um einen Compiler-Fehler. Es geht darum, wie Prozessor und Arbeitsspeicher tatsächlich auf niedriger Ebene arbeiten. In diesem Artikel werden wir das Alignment von Strukturen in C++, das Konzept des Paddings (Auffüllens) behandeln und lernen, die tatsächliche Größe von Strukturen vorherzusagen. Und vor allem werden wir erfahren, wie wir dies steuern können, wenn es wirklich nötig ist.

\n\n

Warum ist 1 + 4 ≠ 5? Die Physik des Prozesses

\n\n

Moderne Prozessoren lesen Speicher nicht byteweise. Sie arbeiten mit "Wörtern" — Blöcken von 4, 8 oder 16 Bytes (abhängig von der Architektur). Wenn eine 4-Byte-Ganzzahl (int) an einer Adresse liegt, die nicht ein Vielfaches von 4 ist, muss der Prozessor zwei Speicherzugriffe statt einem durchführen, um diese Zahl zu lesen und sie dann aus zwei Teilen "zusammenzusetzen". Das ist katastrophal langsam.

\n\n

Um dies zu vermeiden, fügt der Compiler "leere" Bytes (Padding) zwischen die Felder der Struktur ein. Er richtet jedes Feld an einer Adresse aus, die ein Vielfaches seiner Größe ist. Dies wird als natürliches Alignment (natural alignment) bezeichnet.

\n\n
// Einfaches Beispiel, das alle überrascht\n#include <iostream>\n\nstruct Example {\n    char c;   // 1 Byte\n    int  i;   // 4 Bytes\n};\n\nint main() {\n    std::cout << "Größe von char: " << sizeof(char) << std::endl;    // 1\n    std::cout << "Größe von int: "  << sizeof(int)  << std::endl;    // 4\n    std::cout << "Größe von struct: " << sizeof(Example) << std::endl; // 8 (!)\n    return 0;\n}
\n\n

Wie sieht das im Speicher aus? c belegt Byte 0. Byte 1, 2, 3 sind Padding. Und i beginnt bei Byte 4. Insgesamt: 1 (c) + 3 (leer) + 4 (i) = 8 Bytes.

\n\n

Die Regeln des Alignments: Wie der Compiler Entscheidungen trifft

\n\n

Der Compiler folgt einfachen, aber strengen Regeln. Wenn Sie diese kennen, können Sie die Größe einer Struktur vorhersagen, ohne das Programm auszuführen.

\n\n
    \n
  • Regel 1: Die Reihenfolge der Felder bleibt erhalten. Der Compiler vertauscht keine Felder im Speicher (obwohl einige Compiler dies bei Optimierungen tun können, garantiert der Standard dies nicht).
  • \n
  • Regel 2: Jedes Feld wird an einer Adresse ausgerichtet, die ein Vielfaches seiner Größe ist. char (1 Byte) kann überall sein. short (2 Bytes) an geraden Adressen. int (4 Bytes) an Adressen, die ein Vielfaches von 4 sind. double (8 Bytes) an Adressen, die ein Vielfaches von 8 sind.
  • \n
  • Regel 3: Die Größe der gesamten Struktur muss ein Vielfaches des Alignments des größten Feldes sein. Dies ist notwendig, damit Arrays von Strukturen korrekt funktionieren. Wenn die größte Variable ein double (8 Bytes) ist, dann ist die Größe der Struktur ein Vielfaches von 8.
  • \n
\n\n

Lassen Sie uns ein komplexeres Beispiel analysieren:

\n\n
struct Complex {\n    char  a; // 1 Byte\n    int   b; // 4 Bytes\n    short c; // 2 Bytes\n    char  d; // 1 Byte\n};\n// Wie groß wird sizeof sein? Rechnen wir gemeinsam.
\n\n

Schritt-für-Schritt-Analyse:

\n
    \n
  • a (char) — Byte 0.
  • \n
  • b (int) — muss an einer Adresse sein, die ein Vielfaches von 4 ist. Die nächste solche Adresse ist 4. Also Padding: Bytes 1, 2, 3. Summe nach b: 8 Bytes (0-7).
  • \n
  • c (short) — muss an einer geraden Adresse sein. Aktuelle Position ist 8 (gerade). Perfekt. Summe nach c: 10 Bytes (0-9).
  • \n
  • d (char) — kann überall sein. Wir setzen es in Byte 10. Summe nach d: 11 Bytes (0-10).
  • \n

Blogs

Buchempfehlungen