最近在学习汇编语言,和朋友聊天时聊到了 C 语言中的变长数组,想知道在汇编后是什么样子的。

变长数组(Variable-length array)

可变长数组是指在计算机程序设计中,数组对象的长度在运行时(而不是编译时)确定。

C/C++的灵活数组类型(又称柔性数组成员)是另外一个语言特性。

反汇编 VLA

C 语言代码:

#include <stdio.h>
int main(void) {
    int n;
    int a[n];
    return 0;
}

反汇编代码:

...
mov    eax,DWORD PTR [ebp-0x14] ; eax <- [n]
lea    edx,[eax-0x1]            ; edx <- [n - 1]
mov    DWORD PTR [ebp-0xc],edx  ; [n - 8] <- edx
lea    edx,[eax*4+0x0]          ; edx <- [n * 4 + 0] VLA 的长度
mov    eax,0x10                 ; eax <- 16
sub    eax,0x1                  ; eax -= 1 => eax = 15
add    eax,edx                  ; eax += edx => eax = 15 + nbytes
mov    ecx,0x10                 ; ecx <- 16
mov    edx,0x0                  ; edx <- 0
div    ecx                      ; eax /= 16
imul   eax,eax,0x10             ; eax *= 16
                                ; (15 + nbytes) / 16 * 16 用于对齐
                                ; 因此上面几条指令没有抵消
sub    esp,eax                  ; esp -= eax 在栈中为 VLA 预留空间
mov    eax,esp                  ; eax <- esp VLA 的地址
add    eax,0x3                  ; eax += 3 => eax = 18 + nbytes
shr    eax,0x2                  ; eax >>= 2
shl    eax,0x2                  ; eax <<= 2
                                ; (eax + 3) / 2 * 2 用于对齐
                                ; 因此上面几条指令没有抵消
mov    DWORD PTR [ebp-0x10],eax ; [n + 4] <- eax 给 a 赋上 VLA 的地址
...

因此在创建 VLA 时先在栈上分配内存同时要注意对齐,再将分配好的内存地址赋值给数组。

与直接创建在栈上的定长数组不同,变长数组访问元素时需要两次寻址。