如何处理或避免 C++ 中的堆栈溢出
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12146201/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to handle or avoid a stack overflow in C++
提问by Ralph Tandetzky
In C++ a stack overflow usually leads to an unrecoverable crash of the program. 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.
在 C++ 中,堆栈溢出通常会导致程序不可恢复的崩溃。对于需要真正健壮的程序,这是不可接受的行为,特别是因为堆栈大小是有限的。关于如何处理问题的几个问题。
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++ 行为的解决方案的任何其他有用的评论将不胜感激。
采纳答案by James McNellis
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. This exception is not a C++ exception, though, it is an asynchronous exception. 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. This is expected, though, because a stack overflow can occur at any time: any function call or stack allocation may overflow the stack.
请注意,某些平台会在发生堆栈溢出时通知程序并允许程序处理错误。例如,在 Windows 上,会引发异常。此异常不是 C++ 异常,但它是异步异常。C++异常只能由throw
语句抛出,而异步异常则可能在程序执行过程中的任何时候抛出。不过,这是意料之中的,因为堆栈溢出随时可能发生:任何函数调用或堆栈分配都可能使堆栈溢出。
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 (e.g., from functions marked noexcept
or throw()
in C++). 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.
问题是堆栈溢出可能导致异步异常甚至从不期望抛出任何异常的代码中抛出(例如,来自标记的函数noexcept
或throw()
在 C++ 中)。因此,即使您确实以某种方式处理了此异常,您也无法知道您的程序处于安全状态。因此,处理异步异常的最佳方法是根本不处理(*)。如果抛出一个,则意味着程序包含错误。
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.
(*) 有一些非常罕见的例外。
回答by marcinj
You can protect against stack overflows using good programming practices, like:
您可以使用良好的编程实践来防止堆栈溢出,例如:
- Be very carefull with recursion, I have recently seen a SO resulting from badly written recursive CreateDirectory function, if you are not sure if your code is 100% ok, then add guarding variable that will stop execution after N recursive calls. Or even better dont write recursive functions.
- Do not create huge arrays on stack, this might be hidden arrays like a very big array as a class field. Its always better to use vector.
- Be very carefull with alloca, especially if it is put into some macro definition. I have seen numerous SO resulting from string conversion macros put into for loops that were using alloca for fast memory allocations.
- Make sure your stack size is optimal, this is more important in embeded platforms. If you thread does not do much, then give it small stack, otherwise use larger. I know reservation should only take some address range - not physical memory.
- 对递归要非常小心,我最近看到由于递归 CreateDirectory 函数编写不当而导致的 SO,如果您不确定您的代码是否 100% 正常,则添加保护变量,该变量将在 N 次递归调用后停止执行。或者最好不要写递归函数。
- 不要在堆栈上创建巨大的数组,这可能是隐藏的数组,就像一个非常大的数组作为类字段。使用矢量总是更好。
- 使用 alloca 时要非常小心,尤其是当它被放入一些宏定义时。我已经看到很多 SO 是由字符串转换宏放入 for 循环中产生的,这些循环使用 alloca 进行快速内存分配。
- 确保您的堆栈大小是最佳的,这在嵌入式平台中更为重要。如果你的线程做的不多,那么给它小堆栈,否则使用更大的堆栈。我知道保留应该只占用一些地址范围 - 而不是物理内存。
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 查找,您应该能够找到一些静态代码分析工具。
回答by Nathan Andrew Mullenax
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 的汇编语法。
回答by Mark B
C++ is a powerful language, and with that power comes the ability to shoot yourself in the foot. 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.
C++ 是一种强大的语言,有了这种强大的能力,你就可以用脚射击自己了。我不知道有任何便携式机制可以在发生堆栈溢出时检测和纠正/中止。当然,任何此类检测都是特定于实现的。例如,g++ 提供-fstack-protector
帮助监控您的堆栈使用情况。
In general your best bet is to be proactive in avoiding large stack-based variables and careful with recursive calls.
一般来说,最好的办法是主动避免基于堆栈的大型变量并小心递归调用。