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

C++ 编译器如何处理可变长度数组,c++,c,templates,visual-c++,c++11,C++,C,Templates,Visual C++,C++11,这似乎是一个初学者的问题,但我对编译器通常创建可变维度数组的方式感兴趣,就像下面的程序一样 #include<iostream> int main(){ int n; std::cin>>n; int a[n]; } #包括 int main(){ int n; 标准:cin>>n; int a[n]; } 据我所知,在C语言中,所有初始值设定项的值都必须是常量,这样编译器就知道在函数中要保留多少内存,通常是通过减去堆栈指针来容纳数组所包含的元素数 这对

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

#include<iostream>

int main(){
  int n;
  std::cin>>n;
  int a[n];
}
#包括
int main(){
int n;
标准:cin>>n;
int a[n];
}
据我所知,在C语言中,所有初始值设定项的值都必须是常量,这样编译器就知道在函数中要保留多少内存,通常是通过减去堆栈指针来容纳数组所包含的元素数

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


另外,微软的编译器并不以标准兼容而闻名,所以如果它处理上述程序的方式可能是错误的,我也不会感到惊讶

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

GCC允许它作为一个扩展

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

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

在C99版本的C标准中,允许使用可变长度数组。但是,它们不允许在任何版本的C++中使用;您将看到一个G++扩展。请注意,Microsoft的C编译器不完全支持C99;由于G++支持C99,所以将VLA支持应用到C++是很容易的。
int main(){
  int n;
  std::cin>>n;
  int a[n];
}

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

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

早在VLAs成为语言的一部分之前,这种功能就已经存在了。[non-standard]函数
alloca
正是这样做的。唯一的区别是当函数退出时,
alloca
分配的内存会自动释放,而本地VLA必须遵守标准的基于块的生存期规则。后者根本不是问题,因为块嵌套是以类似堆栈的方式进行的

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

int a[n];
本质上被翻译成

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

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

奇怪的是,Visual Studio至少在C模式下支持它,但会发出警告,因为它不是C89.)我认为还有其他缺点,如果我对分解的记忆是正确的,那么它也会引入堆栈变量引用的开销,因为它们通常是使用当前堆栈指针的增量计算的。如果没有VLA,增量是固定的,但是使用VLA,增量取决于数组的大小(对于数组后面的引用),因此在需要计算时会产生开销。@MatthieuM.,更可能的情况是,它只使用一个额外的寄存器来指向堆栈的顶部frame@bdonlan:我想这取决于寄存器压力(特别是x86 32位…)。感谢您指出这一点,我还没有注意到,如果经常使用它,它可能会被保留在一边。@MatthieuM.,是的,归根结底,您必须保持帧指针,这可能会导致寄存器压力略微增加(但在x86上会显着增加)。
alloca(n*sizeof*a)的+1< C++ >代码> > AlcORA(n*sieof of int)及其所有的花式演绎机制,但您有一个问题:当代码被声明为<代码> int [n] < /Cord>时,<代码> >代码>的类型是什么?这与语言的其他部分不太协调,这大概就是为什么它没有包含在标准中的原因