GCC如何实现可变长度数组?

时间:2014-01-17 09:31:14

标签: c arrays gcc

GCC如何实现可变长度数组(VLA)?这些数组是否基本上指向动态分配的存储,例如alloca返回的

我能想到的另一个选择是,这样的数组被分配为函数中的最后一个变量,因此在编译时可以知道变量的偏移量。但是,在编译期间,第二个VLA的偏移量将再次不知道。

2 个答案:

答案 0 :(得分:9)

以下是从some GCC docs for VLA support获取的示例行的分配代码(x86 - x64代码类似):

char str[strlen (s1) + strlen (s2) + 1];

strlen (s1) + strlen (s2) + 1的计算位于eax(GCC MinGW 4.8.1 - 无优化):

mov edx, eax
sub edx, 1
mov DWORD PTR [ebp-12], edx
mov edx, 16
sub edx, 1
add eax, edx
mov ecx, 16
mov edx, 0
div ecx
imul    eax, eax, 16
call    ___chkstk_ms
sub esp, eax
lea eax, [esp+8]
add eax, 0
mov DWORD PTR [ebp-16], eax

所以它看起来基本上是alloca()

答案 1 :(得分:5)

嗯,基于VLA的限制,这些只是在黑暗中的一些狂野刺伤,但无论如何:

VLA不能:

  • 的extern
  • struct members
  • 静态
  • 使用未指定的边界声明(保存为函数原型)

所有这些都指向VLA被分配在堆栈上,而不是堆。所以是的,VLA可能是分配新块时分配的堆栈内存的最后一块(块如块范围,这些是循环,函数,分支或其他)。
这也是为什么VLA会增加Stack溢出的风险,在某些情况下会显着增加(警告:例如,甚至不考虑将VLA与递归函数调用结合使用!)。 这也是为什么越界访问很可能导致问题的原因:一旦块结束,指向 VLA内存的任何内容都指向无效内存。
但是好的方面:这也是为什么这些数组是线程安全的(因为线程有自己的堆栈),以及为什么它们比堆内存更快。

VLA的大小不能是:

  • extern
  • 零或负面

外部限制非常明显,非零,非负面限制......但是:例如,如果指定VLA大小的变量是带符号的int,则编译器不会产生错误:VLA的评估和分配是在运行时期间完成的,而不是编译时。因此 VLA的大小不能,也不需要在编译时给定 正如MichaelBurr正确地指出的那样,VLA与alloca内存非常相似,其中一个是恕我直言,至关重要的区别:alloca分配的内存在分配点和整个函数的其余部分都是有效的。 VLA是块作用域,因此一旦退出使用VLA的块,就会释放内存:

void alloca_diff( void )
{
    char *alloca_c, *vla_c;
    for (int i=1;i<10;++i)
    {
        char *alloca_mem = alloca(i*sizeof(*alloca_mem));
        alloca_c = alloca_mem;//valid
        char vla_arr[i];
        vla_c = vla_arr;//invalid
    }//end of scope, VLA memory is freed
    printf("alloca: %c\n", *alloca_c);//fine
    printf("vla: %c\n\", *vla_c);//undefined behaviour... avoid!
}//end of function alloca memory is freed, irrespective of block scope