为什么在 Visual C++ 中堆栈溢出不会抛出错误?

问题描述 投票:0回答:6

在 Microsoft Visual C++ 2010 中,我创建了一个故意导致堆栈溢出的程序。当我使用“开始调试”运行程序时,发生堆栈溢出时会引发错误。当我使用“启动而不调试”运行它时,不会抛出任何错误,并且程序只是默默地终止,就好像它已成功完成一样。有人可以向我解释一下发生了什么事吗?还有其他编译器不会在堆栈溢出时抛出错误吗?

(我认为这是询问有关堆栈溢出问题的正确位置。)

c++ stack-overflow
6个回答
9
投票

C++ 不会像托管环境那样握住你的手。堆栈溢出意味着未定义的行为。


5
投票

堆栈溢出是未定义的行为。编译器完全有权利忽略它或导致任何事件发生。


4
投票

因为当你的进程堆栈溢出时,它就不再是一个有效的进程了。 显示错误消息需要堆栈。

Raymond Chen 最近讨论过这个问题。

至于为什么调试器能够抛出这样的异常,在这种情况下,进程会被保留,因为它以调试模式附加到调试器的进程。您的进程没有显示错误,而是调试器显示错误。

在Windows机器上,您可以捕获与堆栈溢出相对应的SEH异常。例如,您可以查看 boost::regex 的源代码(Google for BOOST_REGEX_HAS_MS_STACK_GUARD)。


2
投票

编译器很可能优化了预期的堆栈溢出。考虑以下伪代码示例:

void RecursiveMethod(int n)
{
    if (n % 1024 == 0)
        print n;

    // call recursively
    RecursiveMethod(n + 1);
} 

该方法将递归调用自身并很快溢出堆栈,因为没有退出条件。

但是,大多数编译器使用尾递归,一种将递归函数调用转移到循环构造中的技术。

需要注意的是,使用尾递归,上面的程序会无限循环运行,不会默默退出。

Bart de Smet 有一篇不错的博客文章,其中解释了该技术在 .NET 中的工作原理:

演示失败的案例 – x64 上的 StackOverflowException


1
投票

在调试版本中,会进行许多堆栈检查,以帮助您检测堆栈溢出、堆栈损坏等问题。它们在发布版本中不存在,因为它们会影响应用程序的性能。正如其他人指出的那样,堆栈溢出是未定义的行为,因此编译器根本不需要实现此类堆栈检查。

当您处于调试环境中时,运行时检查将帮助您检测发布版本中也会出现的问题,因此,如果您修复了调试版本中检测到的所有问题,那么它们也应该在您的版本中修复建造 。 。 。理论上。在实践中,有时您在调试版本中看到的错误并不存在于您的发布版本中,反之亦然。

堆栈溢出不应该发生。通常,堆栈溢出仅在无意的递归函数调用或在堆栈上分配足够大的缓冲区时发生。前者显然是一个bug,后者应该使用堆来代替。


1
投票

在调试模式下,您需要开销。您希望它检测是否破坏了堆栈、溢出了缓冲区等。此开销内置于调试工具和调试器中。在高级术语中,调试工具是额外的代码和数据,放置在那里以帮助标记错误,调试器用于检测标记的错误并通知用户(当然,除了帮助您调试之外)。

如果您正在运行以发布模式编译的项目,或者没有附加调试器,那么当程序死掉时,没有人会听到程序的尖叫声:)如果森林里有一棵树倒下......

根据您的编程方式,C++ 是没有辅助轮的编程。如果你碰壁了,没有人会告诉你你搞砸了。你只会崩溃并燃烧,或者更糟糕的是,崩溃并在一种非常残废的状态下继续奔跑,而不知道有什么问题。正因为如此,它可以非常快。没有额外的检查或安全防护措施来防止它以处理器的全速和潜力(当然,还有您在程序中编码了多少额外步骤)来处理您的程序。

© www.soinside.com 2019 - 2024. All rights reserved.