当您请求堆栈中的空间超过可用空间时会发生什么情况

What happens when you request more space in the stack than what is available?

本文关键字:空间 什么情况 请求 堆栈      更新时间:2023-10-16

这个问题是基于C/C++内存分配的。

我读到了堆栈和堆之间的区别,有一件事让我很困惑。应该在堆中为大型对象分配内存,但也可以在堆栈中作为局部变量这样做。

从这个线程(程序的C/C++最大堆栈大小)中,我了解到堆栈是有限的,并且限制相对较低(最大7.4MB)。

我用以下程序测试了这个极限:

#include <vector> 
int main() {
std::vector<double> test;
for (int i = 0; i < 5000000; i++){
    test.push_back(i);
}
return 0;
}

分配的总内存为8字节*(5.000.000)=40MB。这似乎不会引发任何错误。我在此资源上阅读(https://software.intel.com/en-us/articles/determining-root-cause-of-sigsegv-or-sigbus-errors),堆栈溢出可能引发分段故障或总线错误。

所以我想,问题是:当你在堆栈中"分配"的内存超出你的能力时,会发生什么?

std::vector在堆上分配内存,而不是在堆栈上。如果你想测试策略分配,最简单的方法是使用这样的程序:

#include <cstdio>
int main(void) {
    char temp[1024*1024*40] = {};
    printf("%sn",temp);
    return 0;
}

(注意:打印是必要的,以防止缓冲区被优化掉。)
这会在堆栈上分配40 MiB,并产生堆栈溢出(请参阅实时)。

另一种方法是递归调用函数。例如:

unsigned factorial_times_2(unsigned n) {
    unsigned result;
    if (n<2u) result=1u;
    result = n * (factorial_times_2(n-1u)/2u);
    return result * 2u;
}
int main(void) {
    return factorial_times_2(~0u)/2u;
}

这是对经典递归阶乘函数的简单修改(已修改,因为现代编译器会使简单阶乘尾递归)。在运行时,它将尝试制作大约40亿个堆栈帧。产生堆栈溢出(请参阅实时)。


正如您所料,堆栈溢出意味着您超出了给堆栈的内存。由于堆栈通常被分配给自己的页面,所以走出堆栈会走出有效的映射内存地址。

因此,堆栈溢出通常会导致分段错误(就像上面的例子中发生的那样)。在Windows上,这被称为访问违规。如果你运气不好,它会破坏你程序的数据,你要等到以后才能发现。

溢出堆栈的行为取决于平台。官方术语可能是"未定义的行为",意思是任何事情都可能发生。

平台不需要实现堆栈,尽管这是一种常见的技术。

有些平台为堆栈和堆留出了内存,并使它们相互"增长"(绘制一幅图)。因此,如果堆栈溢出,它就会开始在堆上进行写入,反之亦然。

一些平台可能设置了硬件围栏,当处理器访问超出范围的内存时,会生成硬件异常。操作系统将处理该异常。

另一个例子是,您的程序开始写入某种硬件设备,例如USB控制器或磁盘驱动器控制器。

总之,堆栈溢出的行为取决于平台,包括恢复(如果有的话)。

相关文章: