问题:C99 中 数组的长度可以动态申请,底层是如何实现的?(我不确定是不是C99)
学过一点函数堆栈图,函数开栈的时候要知道自己要开多大的空间,这就要知道这个函数体中的所有变量要用多大的空间(不是new 、malloc 出来的变量在函数开的栈中),之前做题的时候看到有题解没有malloc 也没有new 直接int a[n],这种是不是编译器主动把它开到堆中了,如果不开在堆中,说不过道理啊?
变长数组是C99的新特性,它是在栈上分配的,下面是一个简单例子,用clang
编译器在x64
架构上生成的代码进行解析。
void f(int n)
{
int a[n];
a[0] = 1;
a[1] = 2;
}
f: # @f
push rbp ; 保存帧指针
mov rbp, rsp ; 当前帧指针
sub rsp, 32 ; 在栈上分配32个字节内存空间
mov dword ptr [rbp - 4], edi ; edi是函数传入的第一个参数,将其赋值给实参/局部变量n,*(rbp-4) = n
mov eax, dword ptr [rbp - 4] ; eax = n
mov ecx, eax ; ecx = n
mov rax, rsp ; rax = rsp
mov qword ptr [rbp - 16], rax ; 保存栈指针rsp到rbp-16,*(rbp-16) = rsp
lea rdx, [4*rcx + 15] ; 接下来两个指令是计算在栈上分配VLA数组的内存大小,rdx=(sizeof(int)*n+15)/16*16,按16字节对齐
and rdx, -16 ;
mov rax, rsp ; 计算rsp = rax = rsp - rdx,即在栈上分配内存,rax是VLA数组首地址
sub rax, rdx ;
mov rsp, rax ;
mov qword ptr [rbp - 24], rcx ; 保存数组长度n
mov dword ptr [rax], 1 ; a[0] = 1
mov dword ptr [rax + 4], 2 ; a[1] = 2
mov rax, qword ptr [rbp - 16] ; rax = 原栈指针
mov rsp, rax ; 恢复栈指针,释放VLA数组内存
mov rsp, rbp ; 释放局部变量内存
pop rbp ; 恢复帧指针
ret ; 函数返回
.
.
.
4 +------------------+
| 调用函数帧指针rbp |
0 +------------------+ <---- 当前函数帧指针rbp
| 实参n |
-4 +------------------+
| |
-8 +------------------+
| 保存的 |
-12 + 栈指针rsp +
| 占8个字节 |
-16 +------------------+
| VLA数组 |
-20 + 元素数目n +
| 占8个字节 |
-24 +------------------+
| |
+------------------+
| | a[n-1]
+------------------+
.
.
.
+------------------+
| 2 | a[1]
+------------------+
| 1 | a[0]
+------------------+ <---- rsp, rax变长数组(VLA)首地址
.
.
.
动态申请是在内存区开辟空间
如果是int a[n],那数组就会保存在栈里,如果malloc就会保存在堆里