变长数组VLA(静态绑定或动态)

自从我用基本数组编写基本编译器以来,已经有很长一段时间了,但最近我看到了这样的数组声明:

int y; cin>>y; int z[y]; 

用于给出错误的旧时编译器“数组的存储大小不是恒定的”。 然后我在C99中发现了可变大小的数组。 我想知道他们如何在内部工作。 这会使arrays动态吗? 这个内存是在堆分配的吗? 这种绑定是否仍然是静态完成的? 如果是这样的话。

这会使arrays动态吗?

这取决于你如何定义“动态”。 VLA当然不能增长或缩小,换句话说,一旦创建就改变它的大小。 但它是动态的,从某种意义上说,它的长度在编译时是未知的。

这个内存是在堆分配的吗?

如何为VLA分配内存是特定于实现的。 一般而言,VLA的存储器是从调用者的堆栈帧中的空间分配的。 然后,当定义了VLA的函数返回其调用者或VLA超出范围时,它将自动释放。

VLA的最近亲属是alloca()函数,它几乎可以被认为具有相同的效果,至少在C中。假设编译器以与实现alloca()相同的方式实现VLA,你可以想到这两个数组在技​​术上与C相同:

 int *a = alloca(sizeof(int) * N); int b[N]; 

然而,VLA具有更紧凑和方便的语法。 但最重要的是,VLA本质上具有自动存储持续时间,并且编译器可以更自由地决定是在离开声明范围时还是从函数返回时销毁/释放数组。

这在诸如C ++之类的语言中变得非常重要,其中编译器实现RAII惯用语并且必须保证在退出其范围时销毁具有自动存储持续时间的对象。

但请注意,VLA目前不是C ++语言的一部分,并且由编译器实现为非标准扩展。 但是它们有望成为C ++ 14的标准

可变长度数组 ( VLA )是C99的一个特性,但是包括gcc在内的几个编译器支持VLA作为 C99之外的扩展 ,并且gcc和clang都支持C ++中的可变长度数组作为扩展,即使这实际上是C99function。

在使用-pedantic标志的gccclang构建中,将产生类似于C中的以下警告:

 warning: variable length arrays are a C99 feature [-Wvla-extension] 

C ++中的类似内容:

 warning: ISO C++ forbids variable length array 'z' [-Wvla] 

通常的实现将在堆栈上分配VLA ,因为它们将像其他自动变量一样对待它们,尽管标准不引用堆栈或堆通常在堆栈中分配大多数情况。

我们还可以从C99草案标准中看到, VLA只是第6.7.5节中数组声明的另一种变体。数组声明4段说( 强调我的 ):

如果大小不存在,则数组类型是不完整类型。 如果大小是*而不是表达式,则数组类型是未指定大小的可变长度数组类型,它只能用于具有函数原型范围的声明; 124)此类数组仍然是完整类型。 如果size是一个整型常量表达式,并且元素类型具有已知的常量大小,则数组类型不是可变长度数组类型; 否则,数组类型是可变长度数组类型。

我们从第5段看到它的大小在它的生命周期中没有变化:

[…]可变长度数组类型的每个实例的大小在其生命周期内不会改变。[…]

虽然与VLA和其他变量的主要区别是sizeof是针对VLA 进行评估的,否则它是在编译时计算的,来自6.5.3.4运算符的sizeof2段:

[…]如果操作数的类型是可变长度数组类型,则评估操作数; 否则,不评估操作数,结果是整数常量。

简短回答:它通过递增堆栈指针来保留堆栈上的空间。