← SEARCHABLE ARCHIVE
Assembly · 2026-08-15

阅读 x86-64 数组、字符串与结构循环

在真实循环中追踪指针与长度契约、stride、sentinel 与边界检查

INTERACTIVE READING MODE →

阅读 x86-64 数组、字符串与结构循环

本文以教学为目的。 示例使用 Intel 语法;pointer validity、object lifetime、aliasing 与 overflow 仍是 C/C++ 边界的契约。

真实 binary 常见“从起始地址以固定 stride 前进,同时检查或累积”的循环。数组、字符串、结构数组共享这个骨架,但终止条件与 stride 不同。

找出四种状态

  1. base/current pointer
  2. index、end pointer 或 remaining count
  3. accumulator 或搜索结果
  4. 终止条件与 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 是 base,r8 是 index,eax 是累积值,unsigned r8 >= rdx 结束循环。

index 与 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

虽然没有显式 index,仍可从初始 pointer、4 字节增量与 end 计算恢复范围。机器加法能 wrap,但 C/C++ pointer arithmetic 只允许更窄的 object 范围。

长度与 sentinel

C 字符串用 0 字节作 sentinel。

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

程序没有最大长度,因此强烈假设可读内存内一定有 NUL。不要混淆字节数、UTF-8 code unit、Unicode code point 与 grapheme cluster。

若搜索失败以 -1 返回,而返回类型是 size_t,其 bit pattern 是 SIZE_MAX。caller 与 callee 必须对 signed -1 和 unsigned 最大值有同一契约。

stride 与字段 offset

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 是字段候选,offset 0 是 float 候选,step 16 是结构大小候选。这是当前 binary layout 的证据,应以 static_assert 与 compiler layout 验证。

AoS 常出现同一 base 的 stride 与多个固定 offset;SoA 常出现每个字段的独立 base。若只批量处理单一字段,改变数据 layout 可能比 handwritten ASM 更有效。

unroll、vector 与 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

一次 machine loop 处理四个元素,通常另有 0~3 个元素的 remainder。不要看到主循环就假设长度一定是 4 的倍数。

movdqu xmm0, [rcx]
paddd  xmm1, xmm0

paddd 表示四个 32 位 lane,不是单一 128 位整数。vectorized loop 常分成 prologue、SIMD main、scalar tail。

overlap 与 size overflow

memcpy 要求不重叠,memmove 必须处理重叠,typed loop 另有 lifetime、alignment、aliasing 契约。

bytes = count * sizeof(Item);

若乘法 wrap 成小 allocation,而循环仍以原 count 前进,就可能造成 heap overflow。要一起审查 allocation size 与 loop bound,并验证 count <= SIZE_MAX / sizeof(Item)。

总结

数据循环可拆成 pointer、stride、accumulator、终止条件。sentinel 循环以 terminator 存在取代显式长度。必须连同空输入、tail、overlap、byte/element 单位与 overflow 才能恢复完整 memory contract。

官方资料