编译器如何处理可变长度数组
这似乎是一个初学者的问题,但我对编译器通常创建变量维数组的方式感兴趣,就像在下面的程序中一样。
#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没有。