【问题标题】:C++ Stack Overflow Exception, possibly due to recursionC++ 堆栈溢出异常,可能是由于递归
【发布时间】:2018-07-24 07:14:09
【问题描述】:

我有一些可以正常工作的代码。但是,有时我的程序由于错误而不得不关闭。我已尝试调试此错误,但在执行此操作时遇到了一些问题。

当我在调试中运行时,我在启动程序后很快就会收到堆栈溢出错误,比在发布时收到错误要早得多。

我已经缩小了在调试中引发错误的点,即第 3570 次调用此递归函数时。

void setRankAbove(vector<nodeStructure> &allNodes, int index, int rankToSet) {
    if (rankToSet>=allNodes[index].rank) {
        allNodes[index].rank = rankToSet;
        if (allNodes[index].root != index) {
            setRankAbove(allNodes, allNodes[index].parent, rankToSet + 1,debug);
        }           
    }
}

关于程序为什么在这里崩溃以及为什么它只在调试时崩溃的任何想法?

无论如何,回到最初的问题,我已经缩小了失败的地方。

vector<vector<double>> backwardsAggregation(int inputRows, int inputCols, vector<vector<double>> &localCostsStepOne, vector<nodeStructure> &nodeMST,float sigma,bool debug) {
    // create 2D array of local costs step 2
    vector<double> localOneDim(inputCols);
    vector<vector<double>> localCostsRootToLeaf(inputRows, localOneDim);

    // factors defined in paper
    double nodeFactor = (1 - exp(-1 / (sigma*sigma)));
    double parentFactor = exp((double)(-0.5) / (sigma*sigma));
    cout << "Step 3.2.1" << endl;
    try{
        aggregateStepTwo(nodeMST, nodeMST[0].root, localCostsStepOne, localCostsRootToLeaf, parentFactor, nodeFactor, debug, 0);
    }
    catch (const std::exception & ex) {
        cout<< "exception"<< endl;
        cout << ex.what() << endl;
    }

    cout << "Step 3.2.2" << endl;
    return localCostsRootToLeaf;
}

在调用一定次数后调用 aggregateStepTwo 失败。它可能会递归调用它 1,000,000 次。这是另一个堆栈溢出吗?

如果是这样,我该如何摆脱堆栈溢出?

谢谢 抢

【问题讨论】:

  • 调试最初可能会在堆栈上放置更多内容,这限制了您拥有的空间。 SO虽然是SO。理想情况下,如果问题大到甚至有可能导致 SO,则不应使用递归。
  • 不同的操作系统会给你不同的默认堆栈大小。 1Mb 的堆栈并不少见。
  • 您始终可以使用表示输入和输出参数的简单结构将递归函数调用替换为循环和std::stack。你应该走这条路。堆栈大小是有限的。好吧,可动态分配的内存也是有限的,但通常更可用。
  • setRankAbove转换为迭代算法,则不会使用栈进行处理。

标签: c++ recursion stack-overflow


【解决方案1】:

你有尾递归。用迭代算法替换这种递归通常非常简单。这是一种天真的方法,它只是将您的递归重写为一个无限循环,该循环在递归停止的相同位置中断。

void setRankAbove(vector<nodeStructure> &allNodes, int index, int rankToSet) {
  while (1) {
    if (rankToSet < allNodes[index].rank) {
      break;
    }
    allNodes[index].rank = rankToSet;
    if (allNodes[index].root == index) {
      break;
    }
    index = allNodes[index].parent;
    rankToSet++;
  }
}

这应该可行。

现在,如果您不喜欢breaks,可以从以下观察中获得更“优雅”的解决方案(值得商榷):

  1. 在第一次迭代中,赋值仅取决于index 的值
  2. 每个后续迭代都取决于index 的根以及涉及index 的父级和rank+1 的关系。

所以第一个看起来像一个特例。其他依赖于涉及indexindex 的父级的相同表达式。因此,如果我们将第一种情况排除在循环之外,我们将得到以下算法:

void setRankAbove(vector<nodeStructure> &allNodes, int index, int rankToSet) {
  if (rankToSet < allNodes[index].rank) {
    return;
  }

  allNodes[index].rank = rankToSet++;
  int parent = allNodes[index].parent;

  while (allNodes[index].root != index && rankToSet >= allNodes[parent].rank) {
    index = parent;
    allNodes[index].rank = rankToSet++;
    parent = allNodes[index].parent;
  }
}

我真的很惊讶编译器没有自动将此递归转换为循环。您是否没有使用优化标志进行编译,还是我对编译器的期望过高?

【讨论】:

    猜你喜欢
    • 2016-02-19
    • 2018-03-25
    • 2017-09-27
    • 2017-01-20
    • 2020-12-17
    • 1970-01-01
    • 2010-12-07
    相关资源
    最近更新 更多