[英]How to handle or avoid a stack overflow in C++
In C++ a stack overflow usually leads to an unrecoverable crash of the program.在 C++ 中,堆栈溢出通常会导致程序不可恢复的崩溃。 For programs that need to be really robust, this is an unacceptable behaviour, particularly because stack size is limited.
对于需要真正健壮的程序,这是不可接受的行为,特别是因为堆栈大小是有限的。 A few questions about how to handle the problem.
关于如何处理问题的几个问题。
Is there a way to prevent stack overflow by a general technique.有没有办法通过通用技术防止堆栈溢出。 (A scalable, robust solution, that includes dealing with external libraries eating a lot of stack, etc.)
(一个可扩展的、健壮的解决方案,包括处理消耗大量堆栈的外部库等)
Is there a way to handle stack overflows in case they occur?如果发生堆栈溢出,有没有办法处理它们? Preferably, the stack gets unwound until there's a handler to deal with that kinda issue.
最好是,在有一个处理程序来处理那个问题之前,堆栈会被解开。
There are languages out there, that have threads with expandable stacks.有一些语言具有可扩展堆栈的线程。 Is something like that possible in C++?
在 C++ 中可能有这样的事情吗?
Any other helpful comments on the solution of the C++ behaviour would be appreciated.对 C++ 行为的解决方案的任何其他有用的评论将不胜感激。
Handling a stack overflow is not the right solution, instead, you must ensure that your program does not overflow the stack.处理堆栈溢出不是正确的解决方案,相反,您必须确保您的程序不会溢出堆栈。
Do not allocate large variables on the stack (where what is "large" depends on the program).不要在堆栈上分配大变量(什么是“大”取决于程序)。 Ensure that any recursive algorithm terminates after a known maximum depth.
确保任何递归算法在已知最大深度后终止。 If a recursive algorithm may recurse an unknown number of times or a large number of times, either manage the recursion yourself (by maintaining your own dynamically allocated stack) or transform the recursive algorithm into an equivalent iterative algorithm
如果递归算法可能递归未知次数或大量次数,请自行管理递归(通过维护自己的动态分配堆栈)或将递归算法转换为等效的迭代算法
A program that must be "really robust" will not use third-party or external libraries that "eat a lot of stack."一个必须“非常健壮”的程序不会使用“占用大量堆栈”的第三方或外部库。
Note that some platforms do notify a program when a stack overflow occurs and allow the program to handle the error.请注意,某些平台会在发生堆栈溢出时通知程序并允许程序处理错误。 On Windows, for example, an exception is thrown.
例如,在 Windows 上,会引发异常。 This exception is not a C++ exception, though, it is an asynchronous exception.
此异常不是 C++ 异常,但它是异步异常。 Whereas a C++ exception can only be thrown by a
throw
statement, an asynchronous exception may be thrown at any time during the execution of a program. C++ 异常只能由
throw
语句throw
,而异步异常可能在程序执行过程中的任何时候抛出。 This is expected, though, because a stack overflow can occur at any time: any function call or stack allocation may overflow the stack.但是,这是意料之中的,因为堆栈溢出随时可能发生:任何函数调用或堆栈分配都可能使堆栈溢出。
The problem is that a stack overflow may cause an asynchronous exception to be thrown even from code that is not expected to throw any exceptions (eg, from functions marked noexcept
or throw()
in C++).问题是堆栈溢出可能导致异步异常被抛出,即使是从预期不会抛出任何异常的代码(例如,来自 C++ 中标记为
noexcept
或throw()
函数)。 So, even if you do handle this exception somehow, you have no way of knowing that your program is in a safe state.因此,即使您确实以某种方式处理了此异常,您也无法知道您的程序处于安全状态。 Therefore, the best way to handle an asynchronous exception is not to handle it at all (*) .
因此,处理异步异常的最佳方法是根本不处理(*) 。 If one is thrown, it means the program contains a bug.
如果抛出一个,则意味着程序包含错误。
Other platforms may have similar methods for "handling" a stack overflow error, but any such methods are likely to suffer from the same problem: code that is expected not to cause an error may cause an error.其他平台可能有类似的方法来“处理”堆栈溢出错误,但任何此类方法都可能遇到相同的问题:预期不会导致错误的代码可能会导致错误。
(*) There are a few very rare exceptions. (*) 有一些非常罕见的例外。
You can protect against stack overflows using good programming practices, like:您可以使用良好的编程实践来防止堆栈溢出,例如:
those are the most SO causes I have seen in past few years.这些是我在过去几年中看到的最SO原因。
For automatic SO finding you should be able to find some static code analysis tools.对于自动 SO 查找,您应该能够找到一些静态代码分析工具。
Re: expandable stacks.回复:可扩展堆栈。 You could give yourself more stack space with something like this:
你可以用这样的东西给自己更多的堆栈空间:
#include <iostream>
int main()
{
int sp=0;
// you probably want this a lot larger
int *mystack = new int[64*1024];
int *top = (mystack + 64*1024);
// Save SP and set SP to our newly created
// stack frame
__asm__ (
"mov %%esp,%%eax; mov %%ebx,%%esp":
"=a"(sp)
:"b"(top)
:
);
std::cout << "sp=" << sp << std::endl;
// call bad code here
// restore old SP so we can return to OS
__asm__(
"mov %%eax,%%esp":
:
"a"(sp)
:);
std::cout << "Done." << std::endl;
delete [] mystack;
return 0;
}
This is gcc's assembler syntax.这是 gcc 的汇编语法。
C++ is a powerful language, and with that power comes the ability to shoot yourself in the foot. C++ 是一种强大的语言,有了这种强大的能力,你就可以用脚射击自己了。 I'm not aware of any portable mechanism to detect and correct/abort when stack overflow occurs.
我不知道有任何便携式机制可以在发生堆栈溢出时检测和纠正/中止。 Certainly any such detection would be implementation-specific.
当然,任何此类检测都是特定于实现的。 For example g++ provides
-fstack-protector
to help monitor your stack usage.例如,g++ 提供
-fstack-protector
来帮助监控您的堆栈使用情况。
In general your best bet is to be proactive in avoiding large stack-based variables and careful with recursive calls.一般来说,最好的办法是主动避免基于堆栈的大型变量并小心递归调用。
I don't think that that would work.我不认为那会奏效。 It would be better to push/pop esp than move to a register because you don't know if the compiler will decide to use eax for something.
push/pop esp 比移动到寄存器更好,因为你不知道编译器是否会决定使用 eax 来做某事。
Here ya go: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/resetstkoflw?view=msvc-160在这里你去: https : //docs.microsoft.com/en-us/cpp/c-runtime-library/reference/resetstkoflw?view=msvc-160
I am not as familiar with how things work under Linux and other platforms.我不太熟悉 Linux 和其他平台下的工作方式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.