为什么堆栈中没有可变大小的数组
Why no variable size array in stack?
我真的不明白为什么我不能在堆栈上有一个可变大小的数组,所以像
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++和扩展下也支持它。
因为语言规范是这么说的。其他都不重要了(用分段来解释是非常错误的,原因各不相同)。
- 为什么调用堆栈数组会导致内存泄漏
- 堆栈和队列是否像C++中的数组一样传递?
- 在函数范围内在堆栈上分配的数组在离开函数时是否总是被释放?
- C++ 对象数组堆栈溢出
- 在堆栈上C++大型多维数组
- 如何在 c++ 中实现堆栈数组?
- 将在堆栈上声明的元素添加到静态数组
- 如何模板化堆栈分配的多态指针数组到接口,包括派生类型的相应点?
- 在C++中使用数组时如何防止堆栈溢出?
- 使用一个内存集数组和单个堆栈在 O(n) 中查找数组的下一个更大元素
- 堆栈数组c++的Strcpy行为
- 使用数组C++的动态堆栈
- C++堆栈中数组之间的间隙
- 如果我使用malloc()而不是堆栈数组,OpenGL不会渲染对象
- 模板堆栈对象的数组;语法
- 在给定程序中降低矢量数组实现堆栈的时间复杂度有哪些不同的可能方法?
- 数组变量周围的堆栈已损坏
- 制作数组堆栈和向量的二维数组
- 指针数组堆栈实现
- C++数组:堆栈与堆