← SEARCHABLE ARCHIVE
Assembly · 2026-08-15

x86-64-Schleifen über Arrays, Strings und Strukturen lesen

Zeiger-Längen-Verträge, Strides, Sentinel und Grenzen in echten Schleifen verfolgen

INTERACTIVE READING MODE →

x86-64-Schleifen über Arrays, Strings und Strukturen lesen

Dieser Artikel ist als Lehrmaterial gedacht. Die Beispiele verwenden Intel-Syntax; Zeigergültigkeit, Objektlebensdauer, Aliasing und Overflow bleiben Verträge an der C/C++-Grenze.

Viele reale Binärschleifen laufen von einer Startadresse mit konstantem Stride weiter und prüfen oder akkumulieren Werte. Arrays, Strings und Strukturarrays teilen dieses Muster, unterscheiden sich aber in Terminierung und Schrittweite.

Vier Zustände finden

  1. Base- oder Current-Pointer
  2. Index, End-Pointer oder Restanzahl
  3. Akkumulator oder Suchergebnis
  4. Abbruchbedingung und signedness
xor eax, eax
xor r8d, r8d
loop_test:
    cmp r8, rdx
    jae loop_done
    add eax, dword ptr [rcx + r8*4]
    inc r8
    jmp loop_test
loop_done:
    ret

rcx ist die Basis, r8 der Index, eax der Akkumulator; unsigned r8 >= rdx beendet die Schleife.

Index und End-Pointer

lea rdx, [rdi + rsi*4]
xor eax, eax
cmp rdi, rdx
je done
loop_begin:
    add eax, dword ptr [rdi]
    add rdi, 4
    cmp rdi, rdx
    jne loop_begin
done:
    ret

Auch ohne expliziten Index lässt sich der Bereich aus Startpointer, Vier-Byte-Schritt und berechnetem Ende rekonstruieren. Maschinenaddition kann wrappen; gültige C/C++-Zeigerarithmetik hat einen engeren Objektvertrag.

Länge und Sentinel

C-Strings enden an einem Nullbyte.

strlen_like:
    mov rax, rcx
scan:
    cmp byte ptr [rax], 0
    je found
    inc rax
    jmp scan
found:
    sub rax, rcx
    ret

Ohne Maximallänge gilt die starke Annahme, dass ein NUL innerhalb lesbaren Speichers liegt. Byteanzahl, UTF-8-Code-Units, Unicode-Codepoints und Graphemcluster sind unterschiedliche Einheiten.

Wird „nicht gefunden“ als -1 zurückgegeben und der Typ ist size_t, entspricht das Bitmuster SIZE_MAX. Caller und Callee müssen signed -1 und unsigned Maximum konsistent interpretieren.

Stride und Feldoffset

xorps xmm0, xmm0
test rdx, rdx
jz done
loop_begin:
    cmp dword ptr [rcx + 12], 0
    je skip
    addss xmm0, dword ptr [rcx]
skip:
    add rcx, 16
    dec rdx
    jne loop_begin
done:
    ret

Offset 12 ist ein Feldkandidat, Offset 0 ein float-Kandidat, Schritt 16 ein Strukturgrößenkandidat. Es sind Belege des aktuellen Binärlayouts, die mit static_assert und Compilerlayout geprüft werden.

AoS zeigt häufig eine Basis mit Stride und mehreren festen Offsets; SoA eher unabhängige Basen je Feld. Für Massenverarbeitung eines Feldes kann Layoutänderung mehr bringen als handgeschriebener Assembler.

Unrolling, Vektoren und Tail

loop4:
    add eax, [rcx]
    add eax, [rcx + 4]
    add eax, [rcx + 8]
    add eax, [rcx + 12]
    add rcx, 16
    sub rdx, 4
    jae loop4

Eine Maschinenschleife verarbeitet vier Elemente. Üblicherweise behandelt ein Restpfad 0–3 Elemente. Aus dem Hauptloop folgt nicht, dass die Länge durch vier teilbar sein muss.

movdqu xmm0, [rcx]
paddd  xmm1, xmm0

paddd bedeutet vier 32-Bit-Lanes, nicht eine 128-Bit-Ganzzahl. Vektorisierte Schleifen bestehen oft aus Prolog, SIMD-Hauptloop und skalarem Tail.

Overlap und Größenüberlauf

memcpy verlangt nicht überlappende Bereiche, memmove muss Overlap behandeln, und typisierte Schleifen haben zusätzlich Lifetime-, Alignment- und Aliasingverträge.

bytes = count * sizeof(Item);

Wrappt die Multiplikation auf eine kleine Allokation, während der Loop den ursprünglichen count nutzt, kann ein Heap Overflow entstehen. Allokationsgröße und Loopgrenze gemeinsam prüfen und count <= SIZE_MAX / sizeof(Item) validieren.

Zusammenfassung

Datenschleifen zerfallen in Pointer, Stride, Akkumulator und Abbruch. Sentinel-Schleifen ersetzen eine explizite Länge durch die Terminatorannahme. Leere Eingaben, Tail, Overlap, Byte-/Elementeinheit und Overflow gehören zum vollständigen Speichervertrag.

Offizielle Quellen