我有一个相当大的递归函数(我也用C编写),虽然我毫不怀疑堆栈溢出发生的情况极不可能,但它仍然是可能的。我想知道你是否可以检测堆栈是否会在几次迭代中溢出,这样你就可以在不崩溃程序的情况下进行紧急停止。
答案 0 :(得分:9)
在C编程语言本身,这是不可能的。一般来说,在用完之前,你很难知道你的堆栈用完了。我建议您在实现中对递归深度设置一个可配置的硬限制,这样您就可以在超出深度时简单地中止。您还可以重写算法以使用辅助数据结构而不是通过递归使用堆栈,这为您提供了更大的灵活性来检测内存不足的情况; malloc()
会告诉您何时失败。
但是,您可以在类UNIX系统上使用类似的过程获得类似的东西:
setrlimit
将软堆栈限制设置为低于硬堆栈限制SIGSEGV
和SIGBUS
建立信号处理程序,以获得堆栈溢出的通知。某些操作系统为这些操作系统生成SIGSEGV
,其他操作系统生成SIGBUS
。setrlimit
提高软堆栈限制并设置一个全局变量以识别出现这种情况。设置变量volatile
,以便优化器不会破坏您的平原。这可能无处不在,需要特定于平台的代码才能发现信号来自堆栈溢出。并非所有系统(特别是早期的68000系统)在获得SIGSEGV
或SIGBUS
后都可以继续正常处理。
Bourne shell使用类似的方法进行内存分配。
答案 1 :(得分:4)
这是一个适用于win-32的简单解决方案。实际上类似于Wossname已发布但不那么icky:)
unsigned int get_stack_address( void )
{
unsigned int r = 0;
__asm mov dword ptr [r], esp;
return r;
}
void rec( int x, const unsigned int begin_address )
{
// here just put 100 000 bytes of memory
if ( begin_address - get_stack_address() > 100000 )
{
//std::cout << "Recursion level " << x << " stack too high" << std::endl;
return;
}
rec( x + 1, begin_address );
}
int main( void )
{
int x = 0;
rec(x,get_stack_address());
}
答案 2 :(得分:3)
这是一个天真的方法,但它有点icky ......
第一次输入函数时,可以存储在该函数中声明的一个变量的地址。将该值存储在函数之外(例如,在全局中)。在后续调用中,将该变量的当前地址与缓存副本进行比较。你越深入,这两个值就会越远。
这很可能会导致编译器警告(存储临时变量的地址),但它确实可以让您以相当准确的方式知道您正在使用多少堆栈。
不能说我真的推荐这个但它会起作用。
#include <stdio.h>
char* start = NULL;
void recurse()
{
char marker = '@';
if(start == NULL)
start = ▮
printf("depth: %d\n", abs(&marker - start));
if(abs(&marker - start) < 1000)
recurse();
else
start = NULL;
}
int main()
{
recurse();
return 0;
}
答案 3 :(得分:2)
另一种方法是在程序开始时学习堆栈限制,并在每次递归函数中检查是否已接近此限制(在某个安全范围内,比如说64 kb)。如果是这样,中止;如果没有,继续。
可以使用getrlimit
系统调用来学习POSIX系统的堆栈限制。
线程安全的示例代码:(注意:它代码假定堆栈向后增长,就像x86
一样!)
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
void *stack_limit;
#define SAFETY_MARGIN (64 * 1024) // 64 kb
void recurse(int level)
{
void *stack_top = &stack_top;
if (stack_top <= stack_limit) {
printf("stack limit reached at recursion level %d\n", level);
return;
}
recurse(level + 1);
}
int get_max_stack_size(void)
{
struct rlimit rl;
int ret = getrlimit(RLIMIT_STACK, &rl);
if (ret != 0) {
return 1024 * 1024 * 8; // 8 MB is the default on many platforms
}
printf("max stack size: %d\n", (int)rl.rlim_cur);
return rl.rlim_cur;
}
int main (int argc, char *argv[])
{
int x;
stack_limit = (char *)&x - get_max_stack_size() + SAFETY_MARGIN;
recurse(0);
return 0;
}
输出:
max stack size: 8388608
stack limit reached at recursion level 174549