使用递归算法的SIGSEGV

SIGSEGV with a recursive algorithm

本文关键字:SIGSEGV 递归算法      更新时间:2023-10-16

我实现了一个递归算法,它运行了大约1730次递归,然后由于一个神秘的SIGSEGV而崩溃。我尝试了gdb,得到了以下输出:

Program received signal SIGSEGV, Segmentation fault.
0x000000000040b7da in Town::get_cur_capacity (this=0x61efe0) at ./solver/Darstellung.cpp:54
54      return left_over_capacity;
(gdb) print 0x61efe0
$1 = 6418400
(gdb) print *0x61efe0
Cannot access memory at address 0x61efe0
(gdb) print this
$2 = (const Town * const) 0x61efe0
(gdb) 

调试器怎么知道它应该是一个const Town指针,但不能访问内存给我一个转储?我很确定这个方法中没有错误,因为它在崩溃之前被使用了几千次,就像程序中的其他函数一样。这有可能是操作系统相关的问题吗?我使用的是Linux Ubuntu 64位。

我的简化算法:

bool solveproblem(ptr_to_model) {
        if( way_one(ptr_to_model) )
              return true;
        if(way_two(ptr_to_model) )
              return true;
        if( check_if_solved)
              return true;
        return false;
}
bool way_one(ptr_to_model) {
     for(go_through_current_problem_configuration) {
     if(check_stuff) {
          ptr_to_model->execute_partial_solution(...); //adds another problem configuration to the stack within the model
          if(solveproblem(ptr_to_model))
                  return true;
          ptr_to_model->redo_last_step();
     }
     }
     return false;
}
bool way_two(...) {
   /*basicly the same as way one*/
}
bool check_if_solve(...) {
       if(problem_solved)
              return true;
       else
              return false;
}

该模型与名称相似,它表示算法通过将新"层"推到其堆栈上所做的所有步骤,该"层"是由旧问题修改(希望简化)的问题,考虑到算法评估的部分解。

如果你在递归中做了1700层,那么你会溢出堆栈并损坏调用参数,这很容易导致这种类型的崩溃。

如果您使用g++,请尝试添加-fstack-protector-all,看看它是否有助于您获得更好的诊断。

编辑:另一个指示符是如果你在gdb内的反向跟踪变成循环或没有任何结果:这是一个强指示,堆栈已经损坏。

作为对评论的回应,没有一种可靠的方法来确定某些东西是堆栈溢出还是"更正常"的堆损坏。显然,如果可用,valgrind始终是解决内存错误的可靠选项。您可以在shell中使用ulimit或(我相信)以编程方式使用setrlimit来配置堆栈限制。请注意,存在硬性上限,并且通常最好更改递归以减少堆栈滥用,而不是增加堆栈大小。

传递给堆栈的参数有多大?在那个深度,如果你在5千米的高度上有8米的堆叠,你可能会溢出来。这对于堆栈变量来说是相当大的,但是可能的。另一种情况是,您可能会通过写入堆栈中存储的缓冲区(通常是字符串缓冲区)的末尾来破坏堆栈。您在return中崩溃的事实表明这是一种可能性。