【问题标题】:How to create endless recursion which will throw stack overflow exception?如何创建将引发堆栈溢出异常的无限递归?
【发布时间】:2013-03-04 21:59:42
【问题描述】:

我正在尝试使用以下程序创建堆栈溢出运行时异常:

void f(int a) {
  cout << a << ", ";
  f(++a);
}

int main () {
  f(0);
  return 0;
}

当我运行这个程序时,我的电脑运行了大约 261824 call stack 然后出现了 command terminated 运行时错误。现在我想知道:

  1. 这是堆栈溢出的一个很好的例子吗?如果是,为什么会出现command terminated 错误?
  2. try,catch堆栈溢出异常怎么办?
  3. 我有很多可用内存;为什么我的堆栈没有使用我所有的内存?
  4. 如何确定堆栈大小对应于我的call stack

【问题讨论】:

    标签: c++ exception stack stack-overflow callstack


    【解决方案1】:

    这些都是实现细节。从技术上讲,C++ 实现不需要堆栈,它只需要自动存储。至少有一个 C 实现使用堆中的链表(嗯,有点——据我了解,这是一个奇怪的系统)来进行自动存储。

    但是,通常,堆栈是内存地址空间的连续区域,进程保留该区域仅用于存储自动变量和调用帧。它必须在其他任何事情发生之前被保留,因为它必须是连续的,并且如果为其他目的分配了一些内存块,堆栈将无法增长。

    如果您想将整个内存地址空间用于堆栈,则堆(也称为空闲存储)将没有空间。所以堆栈不会使用所有内存...

    1 MB 是设置堆栈的传统值:很少有程序真正需要更多,甚至适度避免将大量数据放入堆栈。在多线程环境中,每个线程最终都有自己的堆栈:因此保持较小的堆栈也会使线程更便宜。现代系统可能会将其设置得更大,因为它们为每个进程提供了大量的地址空间。

    在 64 位系统上,为堆栈使用 50 位地址空间相对容易(这比您的计算机目前可以处理的要多:谷歌数据中心处理 PB 字节)。但这样做的缺点是,您只会在调试时破坏堆栈, 整个系统的虚拟内存被那个进程占用。这样做的好处并不是那么大。

    堆栈的大小是由实现定义的,而不是由 C++ 标准公开的。请参阅您的编译器文档,了解如何确定它的大小以及如何更改其大小。

    C++ 标准并没有规定当您炸毁堆栈时会发生什么。一般来说,当堆栈被炸毁时,你可能会遇到严重的麻烦:编写代码使其不会发生,而不是在它发生后捕捉它。

    【讨论】:

    • 不知道使用堆链表作为堆栈的 C 实现!
    • @Yakk,我不知道我是不是误会你了,但是 2^50 并没有那么大(它只是一个 PB)...
    • 是的,我偏离了几个数量级。糟糕!
    【解决方案2】:
    1. 是的,这是堆栈溢出。您收到的错误消息取决于平台 - 因此,如果您在不同类型的机器上运行它,您可能会看到“堆栈溢出”。
    2. 没有像seen here 这样的便携方式。这是因为您没有抛出异常,而是让操作系统以任何它想要的方式杀死您的进程。
    3. 您的堆栈限制远小于可用内存(堆栈通常为 8-20MB)。在特定的操作系统设置中,如果您确实需要更大的值,您可以重新配置此值(Solaris 上的 ulimit -s 100000 将其设置为 100MB)。但通常达到堆栈限制意味着你做错了什么。一个小的堆栈限制有助于操作系统使用虚拟内存分配方案,并且是堆栈溢出的早期错误捕获器(想象一个较慢的代码版本在一两个小时内消耗所有可用内存......对于其他运行的东西来说不是那么有趣)。
    4. 这可能取决于操作系统或编译器,并不是 C++ 固有的内容。尝试this SO link 了解确定堆栈大小的各种方法。

    【讨论】:

      猜你喜欢
      • 2016-02-19
      • 2018-03-25
      • 2017-09-27
      • 2010-12-07
      • 2017-09-05
      • 1970-01-01
      • 1970-01-01
      • 2020-12-17
      • 2018-07-24
      相关资源
      最近更新 更多