为什么在 Visual C++ 中堆栈溢出不会引发错误?
在 Microsoft Visual C++ 2010 中,我创建了一个故意导致堆栈溢出的程序。当我使用“开始调试”运行程序时,发生堆栈溢出时会引发错误。当我使用“启动而不调试”运行它时,不会抛出任何错误,并且程序只是默默地终止,就好像它已成功完成一样。有人可以向我解释一下发生了什么事吗?还有其他编译器不会在堆栈溢出时抛出错误吗?
(我认为这是询问有关堆栈溢出问题的正确位置。)
In Microsoft Visual C++ 2010 I created a program which delibrately causes a stack overflow. When I run the program using "start debugging" an error is thrown when stack overflow occurs. When I run it with "start without debugging" no error is thrown and the program just terminates silently as if it had successfully completed. Could someone explain to me what's going on? Also do any other compilers not throw errors on stack overflow?
(I thought this would be the right place to ask a question about stack overflow.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
C++ 不会像托管环境那样抓住你的手。堆栈溢出意味着未定义的行为。
C++ won't hold your hand as a managed enviroment does. Having a stack overflow means undefined behaviour.
堆栈溢出是未定义的行为。编译器完全有权利忽略它或导致任何事件发生。
A stack overflow is undefined behaviour. The compiler is well within it's rights to ignore it or cause any event to happen.
因为当你的进程堆栈溢出时,它就不再是一个有效的进程了。 显示错误消息需要堆栈。
Raymond Chen最近讨论过这个问题。
至于为什么调试器能够抛出这样的异常,在这种情况下,进程会被保留,因为它以调试模式附加到调试器的进程。您的进程没有显示错误,而是调试器显示错误。
在 Windows 计算机上,您可以捕获与堆栈溢出相对应的 SEH 异常。例如,您可以查看 boost::regex 的源代码(Google for BOOST_REGEX_HAS_MS_STACK_GUARD)。
Because when your process stack overflows, it is no longer a valid process. Displaying an error message requires a stack.
Raymond Chen went over this recently.
As for why the debugger is able to throw such an exception, in that case the process is being kept around because it's attached in debugging mode to the debugger's process. Your process isn't displaying the error, the debugger is.
On Windows machines, you can catch the SEH exception which corresponds to a stack overflow. For an example, you can see boost::regex's source code (Google for BOOST_REGEX_HAS_MS_STACK_GUARD).
编译器很可能优化了预期的堆栈溢出。考虑以下伪代码示例:
该方法将递归地调用自身并很快溢出堆栈,因为没有退出条件。
然而,大多数编译器使用尾递归,一种将转移递归函数调用循环结构。
需要注意的是,使用尾递归,上面的程序将无限循环地运行,并且不会静默退出。
Bart de Smet 有一篇很好的博客文章,其中解释了该技术在 .NET 中的工作原理:
It might well be that the compiler optimized the intended stack overflow away. Consider the following pseudo-code example:
The method will call itself recursively and overflow the stack pretty quickly because there is no exit condition.
However, most compilers use tail recursion, a technique which will transfer the recursive function call into a loop construct.
It should be noted that with tail recursion, the above program would run in an endless loop and not exit silently.
Bart de Smet has a nice blog article where explains how this technique works in .NET:
在调试版本中,会进行许多堆栈检查,以帮助您检测堆栈溢出、堆栈损坏等问题。它们不会出现在发布版本中,因为它们会影响应用程序的性能。正如其他人指出的那样,堆栈溢出是未定义的行为,因此编译器根本不需要实现此类堆栈检查。
当您处于调试环境中时,运行时检查将帮助您检测发布构建中也会出现的问题,因此,如果您修复了调试构建中检测到的所有问题,那么它们也应该在发布构建中修复。 。 。理论上。在实践中,有时您在调试版本中看到的错误并不存在于发布版本中,反之亦然。
堆栈溢出不应该发生。通常,堆栈溢出仅在无意的递归函数调用或在堆栈上分配足够大的缓冲区时发生。前者显然是一个bug,后者应该使用堆来代替。
In a debug build, a number of stack checks are put in place to help you detect problems such as stack overflows, stack corruption etc. They are not present in release builds because they would affect the performance of the application. As others have pointed out, a stack overflow is undefined behaviour, so the compiler is not required to implement such stack checks at all.
When you're in a debugging environment, the runtime checks will help you detect problems that will also occur in your release build, therefore, if you fix all the problems detected in your debug build, then they should also be fixed in your release build . . . in theory. In practice, sometimes bugs that you see in your debug build are not present in your release build or vice versa.
Stack overflows shouldn't happen. Generally, stack overflows occur only by unintentional recursive function calling, or by allocating a large enough buffer on the stack. The former is obviously a bug, and the latter should use the heap instead.
在调试模式下,您需要开销。您希望它检测是否破坏了堆栈、溢出了缓冲区等。此开销内置于调试工具和调试器中。在高级术语中,调试工具是额外的代码和数据,放置在那里以帮助标记错误,调试器用于检测标记的错误并通知用户(当然,除了帮助您调试之外)。
如果您正在运行以发布模式编译的项目,或者没有附加调试器,那么当您的程序死亡时,没有人会听到它的尖叫声:) 如果一棵树倒在森林里...
取决于您的编程方式, C++ 是没有辅助轮的编程。如果你碰壁了,没有人会告诉你你搞砸了。你只会崩溃并燃烧,或者更糟糕的是,崩溃并在一种非常残废的状态下继续奔跑,而不知道有什么问题。正因为如此,它可以非常快。没有额外的检查或安全防护措施来防止它以处理器的全速和潜力(当然,还有您在程序中编码了多少额外步骤)来处理您的程序。
In debugging mode, you want the overhead. You want it to detect if you broke your stack, overflowed your buffers, etc. This overhead is built into the debug instrumentation, and the debugger. In high level terms the debug instrumentation is extra code and data, put there to help flag errors, and the debugger is there to detect the flagged errors and notify the user (in addition to helping you debug, of course).
If you are running with the project compiled in release mode, or without a debugger attached, there is no one to hear the screaming of your program when it dies :) If a tree falls in the forest...
Depending on how you program, C++ is programming without training wheels. If you hit a wall, no one is going to be there to tell you that you screwed up. You will just crash and burn, or even worse, crash and keep running along in a very crippled state, without knowing anything is wrong. Because of this, it can be very fast. There are no extra checks or safe guards to keep it from blazing through your program with the full speed and potential of the processor (and, of course, how many extra steps you coded into your program).