自从我使用基本数组编写基本编译器以来已经很长时间了,但是最近我看到了这样的数组声明:
int y;
cin>>y;
int z[y];
旧时编译器用于给出错误“数组的存储大小不是常数”。然后我在C99中发现了可变大小的数组。我想知道他们如何在内部工作。这会使阵列动态吗?这个内存是在堆分配的吗?这种绑定是否仍然是静态完成的?如果是这样的话。
答案 0 :(得分:4)
Variable Length Array( VLA )是 C99 功能,但包含gcc
的多个编译器支持以外的VLA as an extension C99 以及gcc和clang都支持 C ++ 中的可变长度数组作为扩展,即使这实际上是 C99 功能。
在使用gcc
标记的clang
和-pedantic
建筑物中,将在 C 中产生类似于以下内容的警告:
warning: variable length arrays are a C99 feature [-Wvla-extension]
和 C ++ 中的类似内容:
warning: ISO C++ forbids variable length array ‘z’ [-Wvla]
通常的实现会在堆栈上分配 VLA ,因为它们会像其他自动变量一样对待它们,虽然标准通常不引用堆栈或堆在大多数情况下,在堆栈上分配。
我们还可以从C99 draft standard看到 VLAs 只是来自6.7.5.2
数组声明符段 4 说(强调我的):
如果大小不存在,则数组类型为不完整类型。如果大小是*而不是表达式,则数组类型是未指定大小的可变长度数组类型,只能在具有函数原型范围的声明中使用; 124)此类数组仍然是完整类型。如果size是一个整型常量表达式,并且元素类型具有已知的常量大小,则数组类型不是可变长度数组类型; 否则,数组类型是可变长度数组类型。
我们从段落 5 看到它的大小在其生命周期内没有变化:
[...]可变长度数组类型的每个实例的大小在其生命周期内不会改变。[...]
虽然与 VLAs 和其他变量的主要区别在于 sizeof 在编译时计算,来自6.5.3.4
部分sizeof运算符段 2 :
[...]如果操作数的类型是可变长度数组类型,则计算操作数;否则,不评估操作数,结果是整数常量。
答案 1 :(得分:3)
这会使数组动态化吗?
这取决于你如何定义“动态”。 VLA当然不能增长或缩小,换句话说,一旦创建就改变其大小。但它是动态的,从某种意义上说它的长度在编译时是不知道的。
这个内存是在堆上分配的吗?
如何为VLA分配内存是特定于实现的。一般而言,VLA的存储器是从调用者的堆栈帧中的空间分配的。然后,当定义了VLA的函数返回其调用者,或者VLA超出范围时,它将自动释放。
VLA的最近亲属是alloca()
函数,几乎可以认为它具有相同的效果,至少在C中。假设编译器实现VLA的方式与实现alloca()
的方式相同,你可以认为这两个数组在技术上在C中是相同的:
int *a = alloca(sizeof(int) * N);
int b[N];
然而,VLA具有更紧凑和方便的语法。但最重要的是,VLA本质上具有自动存储持续时间,并且编译器可以更自由地决定是在离开声明范围时还是从函数返回时销毁/释放数组。
这在C ++这样的语言中变得非常重要,其中编译器实现RAII idiom并且必须保证在退出其范围时销毁具有自动存储持续时间的对象。
但请注意,VLA当前不是C ++语言的一部分,并且由编译器实现为非标准扩展。 但它们是expected to become standard in C++14 。
答案 2 :(得分:1)
简短回答:它通过增加堆栈指针来保留堆栈上的空间。