C语言数组申请的问题

问题: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就会保存在堆里