为什么堆栈中没有可变大小的数组

Why no variable size array in stack?

本文关键字:数组 堆栈 为什么      更新时间:2023-10-16

我真的不明白为什么我不能在堆栈上有一个可变大小的数组,所以像

foo(int n) {
   int a[n];
}

正如我所理解的部分数据段的堆栈(段),因此它不是"恒定大小"。

可变长度数组(VLA)根据c++标准在c++中是不允许的。
包括gcc在内的许多编译器都支持它们作为编译器扩展,但是需要注意的是,任何使用这种扩展的代码都是不可移植的。

c++提供了std::vector来实现与VLA类似的功能。


有一个建议在c++ 11中引入可变长度数组,但最终被放弃了,因为它需要对c++的类型系统进行大量更改。能够在堆栈上创建小数组而不浪费空间或为未使用的元素调用构造函数的好处被认为不足以在c++类型系统中进行大的更改。

我将用一个例子来解释这一点:

假设你有这个函数:

int myFunc() {
   int n = 16;
   int arr[n];
   int k = 1;
}

当程序运行时,它以这种方式将变量设置到堆栈上:

- n @relative addr 0
- arr[16] @relative addr 4
- k @relative addr 64
TOTAL SIZE: 68 bytes

假设我想将arr的大小调整为4个元素。我要做:

delete arr;
arr = new int[4];

现在:如果我以这种方式离开堆栈,堆栈将有未使用空间的洞。所以最明智的做法是把所有的变量从堆栈中的一个位置移到另一个位置,然后重新计算它们的位置。但是我们遗漏了一些东西:c++不会动态地设置位置,它只在编译程序时完成一次。为什么?这很简单:因为实际上不需要在堆栈上放置可变大小的对象,而且在分配/重新分配堆栈空间时会减慢所有程序的速度。

这不是唯一的问题,还有一个更大的问题:当你分配一个数组时,你决定它将占用多少空间,如果你超过了可用空间,编译器会警告你,相反,如果你让程序在你的堆栈上分配可变大小的数组,你就打开了安全漏洞,因为你使所有使用这种方法的程序都容易受到堆栈溢出的影响。

请注意,该提案被拒绝,以下内容不再正确。

N3639中描述的VLA已经在Bristol会议上被接受,并将成为c++ 14的一部分,以及一个库对应部分"dynarray"。因此,使用支持c++ 14的编译器,我们可以开始这样写:

void func(int n)
{
    int arr[n];
}

或者使用dynarray:

#include <dynarray>
void func(int n)
{
    std::dynarray<int> arr(n);
}

简单的回答:因为它在c++标准中没有定义。

没有这么简单的答案:因为没有人提出在这种情况下c++的行为是一致的。从标准POV中没有堆栈,它可以完全不同地实现。C99有vla,但是它们看起来太复杂了,以致于gcc直到4.6才完成实现。我认为没有多少人会愿意为c++提出一些东西,并看到编译器制造商为之奋斗多年。

堆栈是相当小的,它们的大小在不同的体系结构中变化很大。问题是,它是相当容易的"过度分配",并导致段错误或写入内存超过别人拥有。同时,该问题的解决方案(如vector)早已存在。

顺带一提,我读到Stroustrup说他不想要,但我不知道这是在哪个面试中。

因为在c++中静态数组需要一个静态常量大小,所以这是语言不允许的。注意,C99不支持栈上的变量数组,一些实现在c++和扩展下也支持它。

因为语言规范是这么说的。其他都不重要了(用分段来解释是非常错误的,原因各不相同)。