在Microsoft Visual C ++ 2010中,我创建了一个程序,它会导致堆栈溢出。当我使用“开始调试”运行程序时,发生堆栈溢出时会引发错误。当我使用“start without debugging”运行它时,不会抛出任何错误,程序只是静默终止,就像它已成功完成一样。有人可以向我解释发生了什么事吗?还有其他任何编译器不会在堆栈溢出时抛出错误吗?
(我认为这是提出有关堆栈溢出的问题的正确位置。)
答案 0 :(得分:9)
C ++不会像托管环境那样牵着你的手。堆栈溢出意味着未定义的行为。
答案 1 :(得分:5)
堆栈溢出是未定义的行为。编译器完全有权忽略它或导致任何事件发生。
答案 2 :(得分:4)
因为当进程堆栈溢出时,它不再是一个有效的进程。 显示错误消息需要堆栈。
Raymond Chen went over this recently.
至于为什么调试器能够抛出这样的异常,在这种情况下,进程被保留,因为它以调试模式附加到调试器的进程。您的进程没有显示错误,调试器是。
在Windows机器上,您可以捕获与堆栈溢出相对应的SEH异常。例如,您可以看到boost :: regex的源代码(Google for BOOST_REGEX_HAS_MS_STACK_GUARD)。
答案 3 :(得分:2)
很可能编译器优化了预期的堆栈溢出。请考虑以下伪代码示例:
void RecursiveMethod(int n)
{
if (n % 1024 == 0)
print n;
// call recursively
RecursiveMethod(n + 1);
}
该方法将递归调用自身并快速溢出堆栈,因为没有退出条件。
但是,大多数编译器使用tail recursion,这种技术将递归函数调用转换为循环结构。
应该注意的是,使用尾递归,上面的程序将在无限循环中运行,而不是静默退出。
Bart de Smet有一篇很好的博客文章,解释了这种技术在.NET中是如何运作的:
<强> The Case of The Failed Demo – StackOverflowException on x64 强>
答案 4 :(得分:1)
在调试模式下,您需要开销。您希望它检测您是否损坏了堆栈,溢出了缓冲区等。此开销内置于调试工具和调试器中。在高级术语中,调试工具是额外的代码和数据,用于帮助标记错误,调试器可以检测标记的错误并通知用户(当然除了帮助您调试)。
如果您正在使用在发布模式下编译的项目运行,或者没有附加调试器,那么当程序死亡时,没有人可以听到程序的尖叫声:)如果一棵树落在森林中......
根据您的编程方式,C ++编程时没有训练轮。如果你撞墙,没有人会在那里告诉你,你搞砸了。你将崩溃和燃烧,甚至更糟糕的是,崩溃并继续在一个非常残废的状态下运行,而不知道什么是错的。因此,它可以非常快。没有额外的检查或安全防护措施,以防止它在处理程序的全部速度和潜力(以及当然,您编写到程序中的多少额外步骤)的程序中迸发出来。
答案 5 :(得分:1)
在调试版本中,会进行大量的堆栈检查以帮助您检测堆栈溢出,堆栈损坏等问题。它们不会出现在发布版本中,因为它们会影响应用程序的性能。正如其他人所指出的,堆栈溢出是未定义的行为,因此编译器根本不需要实现这样的堆栈检查。
当您处于调试环境中时,运行时检查将帮助您检测发布版本中也会发生的问题,因此,如果您修复了调试版本中检测到的所有问题,那么它们也应该在你的发布版本。 。 。理论上。实际上,有时您在调试版本中看到的错误在您的发布版本中不存在,反之亦然。
不应发生堆栈溢出。通常,堆栈溢出仅通过无意的递归函数调用或通过在堆栈上分配足够大的缓冲区而发生。前者显然是一个bug,而后者应该使用堆。