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

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

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

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

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

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

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

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

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

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

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

 int a[n]; 

基本上被翻译成类似的东西

 int *a = alloca(n * sizeof *a); 

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

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

GCC允许它作为扩展。

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

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