在堆栈已满之前,在C/C 中以最大递归函数调用并给出分段故障



我在做一个问题,我使用递归函数来创建细分树。对于较大的值,它开始给出分割故障。因此,我以前想到这可能是因为数组索引值不合时宜,但后来我认为这可能是因为程序堆栈的变化太大了。我编写了此代码以计算系统给出seg fault之前允许的最大递归调用数量。

#include<iostream>
using namespace std; 
void recur(long long int);
int main()
{
  recur(0);
  return 0;
}
void recur(long long int v)
{
  v++;
  cout<<v<<endl;
  recur(v);
}

运行上述代码后,我的V值为V为261926和261893和261816,然后获得分割故障,所有值都接近这些。

现在,我知道这将取决于机器的机器,以及被调用的功能堆栈的大小,但是有人可以解释如何保护如何免受SEG错误的基础知识,什么是一个软限制请记住。

您可以做的递归级别的数量取决于呼叫堆栈大小与放置的本地变量和参数的大小相结合在这样的堆栈上。除了与许多其他与内存有关的事物一样,"代码的编写方式"外,这非常取决于您正在运行的系统,所使用的编译器,优化级别[1]等。我处理过的一些嵌入式系统,堆栈将是几百个字节,我的第一台家用计算机具有256个字节的堆栈,现代台式机上有堆栈的Megabytes(您可以对其进行调整,但最终您会用完了)<)

在无限的深度上进行递归不是一个好主意,您应该考虑将代码更改为"不做"。您需要了解该算法并了解它将反复出现的深度,以及在您的系统中是否可以接受。不幸的是,当时堆栈用完了任何人都无法做的事情(最多,您的程序崩溃了,最糟糕的是没有,但是会导致其他问题出现问题,例如其他某些应用程序的堆栈或堆都会弄乱!)

在台式机上,我认为拥有一个递归深度达到一百到数千的深度是可以接受的,但不超过这一点 - 也就是说,如果您在每个呼叫中都有少量堆栈的用法 - 呼叫正在使用堆栈的千目标,您应该进一步限制呼叫级别,或者减少堆栈空间的需求。

如果您需要具有比这更多的递归深度,则需要重新安排代码 - 例如,使用软件堆栈存储状态,并在代码本身中循环。

[1]在您发布的代码上使用g -o2使用,我达到5000万,并且我希望如果我离开足够长的时间,它将以零重新启动,因为它会永远继续下去 - 这是因为G 检测到这一点递归可以转换为循环,并做到这一点。使用-O0或-O1编译的同一程序确实确实停在200000年以上。Clang -O1持续下去。当我完成剩余的代码时,Clang-Compried代码仍在运行,以1.85亿个"递归"。

没有(afaik)没有良好的限制。(我从Linux桌面的角度回答)。

在台式机上,笔记本电脑在2015年的默认堆栈大小为几个兆字节。在Linux上,您可以使用setrlimit(2)更改它(对于合理的数字,不要指望能够将其设置为Gigabyte几天) - 您可以使用getrlimit(2)或解析/proc/self/limits(请参阅Proc(5))查询它。在嵌入式微控制器上 - 或在Linux内核内,整个堆栈可能会更加限制(总共几千键)。

使用pthread_create创建线程(3)时,您可以使用显式pthread_attr_t并使用pthread_attr_setstack(3)设置堆栈空间。

顺便说一句,使用最近的GCC,您可以将所有软件(包括标准C库)编译为拆分堆栈(因此将-fsplit-stack传递给gccg++

最后,您的示例是尾声,GCC可以优化(参数跳跃)。我检查了,如果您使用g++ -O2编译(在Linux/X86-64/Debian上使用GCC 4.9.2),则将递归转换为真正的环路,并且没有堆栈分配将无限期地增长(您的程序拨打了近400亿个电话,请访问recur的近4000万个电话。一分钟后,我用诸如方案或OCAML之类的更好的语言中断了它,可以保证尾部呼叫确实是迭代的(然后,尾部递归呼叫通常成为通常的 - 甚至是唯一的环形构造)。

>

网络pok的评论过多(暗示避免递归)。递归非常有用,但是您应该将它们限制在合理的深度(例如几千),并且您应该注意呼叫堆栈上的呼叫框架很小(每个小于一千键),因此实际上分配和分配了大部分分配和交易分配。C堆中的数据。GCC -Fstack -Usage选项对于报告每个编译函数的堆栈使用真的很有用。看到这个并回答。

请注意,持续传递样式是一种将递归转换为迭代的规范方法(然后,您可以通过动态分配的封闭方式来交易堆栈框架)。

一些巧妙的算法用精美的修改迭代替换了递归,例如Deutche-Shorr-Waite图标记算法。

用于基于Linux的应用程序,我们可以使用GetRlimit和SetRlimit API来了解各种内核资源限制,例如大小核心文件,CPU时间,堆栈大小,良好值,最大值。不。" rlimit_stack"的过程等是Linux内核中定义的堆栈的资源名称。以下是检索堆栈大小的简单程序:

#include <iostream>
#include <sys/time.h>
#include <sys/resource.h>
#include <errno.h>
using namespace std;
int main()
{
   struct rlimit sl;
   int returnVal = getrlimit(RLIMIT_STACK, &sl);
   if (returnVal == -1)
   {
      cout << "Error. errno: " << errno << endl;
   }
   else if (returnVal == 0)
   {
      cout << "stackLimit soft - max : " << sl.rlim_cur << " - " << sl.rlim_max << endl;
   }
}

相关内容

  • 没有找到相关文章

最新更新