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

variable length arrays VLA (static binding or dynamic)

本文关键字:静态绑定 动态 VLA 数组      更新时间:2023-10-16

我已经很久没有用基本数组在基本编译器中编程了,但最近我看到了这样的数组声明:

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

旧的编译器常常给出错误"数组的存储大小不是恒定的"。然后我发现了C99中的可变大小数组。我想知道他们内部是如何运作的。这会使数组具有动态性吗?这个内存是按堆分配的吗?这个绑定仍然是静态完成的吗?如果是,如何。

可变长度数组(VLA)是C99的一个功能,但包括gcc在内的一些编译器支持VLA作为C99之外的扩展,gcc和clang都支持C++中的可变长度数组作为扩展,尽管这实际上是C99的一项功能。

gccclang建筑中,使用-pedantic标志将产生类似于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.2数组声明器4段中数组声明的另一个变体(emphasis mine):

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

我们从5段中看到,它的大小在其生命周期内不会改变:

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

尽管与VLA和其他变量的主要区别在于,sizeofVLAN评估的,而在其他情况下,它是在编译时计算的,从6.5.3.4部分开始。sizeof运算符2段:

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

这会使数组具有动态性吗?

这取决于你如何定义"动态"。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中的标准

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