编译器如何处理可变长度数组

时间:2011-10-02 15:24:22

标签: c++ c templates visual-c++ c++11

这似乎是一个初学者的问题,但我对编译器通常创建变量维数组的方式感兴趣,如下面的程序。

#include<iostream>

int main(){
  int n;
  std::cin>>n;
  int a[n];
}

根据我所知,在C中,所有初始值都必须是常量,以便编译器知道在函数内保留多少内存,通常是通过减去堆栈指针来容纳元素的数量数组成立。

这对我有意义。 但是,我不太了解编译器如何处理上述程序,因为它似乎与G ++(MinGW)一起使用,但是使用Cl,Microsoft的C ++编译器失败了。我怀疑GCC通过非标准扩展在堆上分配内存,但我不确定。

此外,微软的编译器并不因标准兼容而闻名,所以如果它对待上述程序的方式实际上可能出错,我不会感到惊讶。

4 个答案:

答案 0 :(得分:12)

在C标准的C99版本中,允许使用可变长度数组。但是,它们不允许在任何版本的C ++中使用;你正在看一个G ++扩展。请注意,Microsoft的C编译器不完全支持C99;由于G ++支持C99,因此很容易将VLA支持应用于C ++作为扩展。

关于编译器通常如何实现VLA,它与alloca()相同(除了它必须保持sizeof的大小) - 编译器保存原始堆栈指针,然后调整它然而,它计算出它需要的许多字节。缺点是函数进入和退出有点复杂,因为编译器需要存储重置堆栈指针的位置,而不是仅仅通过固定常量进行调整。

答案 1 :(得分:5)

运行时“运行时减去堆栈指针”绝对没有问题。这就是编译器通常如何实现可变长度数组。当实际的数组大小已知时,它们在运行时“减去堆栈指针”。这里的所有都是它的。 (没有必要在堆上分配内存,我不知道是什么让你在GCC中怀疑这一点。)。

在VLA成为该语言的一部分之前很久就可以使用这种功能。 [非标准]函数alloca就是这样做的。唯一的区别是alloca分配的内存在函数退出时自动解除分配,而本地VLA必须遵守基于标准块的生存期规则。后者根本不是问题,因为以类似堆栈的方式阻塞嵌套。

换句话说,对于运行时值n声明

int a[n];

基本上被翻译成类似

的东西
int *a = alloca(n * sizeof *a);

加上一些额外的家庭数据来支持sizeof等的功能(当然,还可以在封闭块的末尾自动恢复原始堆栈指针值。)

答案 2 :(得分:2)

没有版本的C ++允许可变长度数组。只有C99允许它。

GCC允许它作为扩展名。

答案 3 :(得分:2)

int main(){
  int n;
  std::cin>>n;
  int a[n];
}

这不是合法的C ++。 G ++接受可变长度数组作为扩展,但是VLA不是C ++标准的一部分。它们是GCC支持的C99标准的一部分(我不确定在多大程度上),但MSVC没有。