为啥这个记忆代码段错误?

Posted

技术标签:

【中文标题】为啥这个记忆代码段错误?【英文标题】:Why does this memoized code segfault?为什么这个记忆代码段错误? 【发布时间】:2016-08-08 21:57:09 【问题描述】:

我有以下代码计算给定 n 和 k 的第二类斯特林数,

#include <cstdint>
#include <map>

#include <boost/multiprecision/cpp_int.hpp>

namespace mp = boost::multiprecision;


mp::cpp_int stirlingS2(unsigned n, unsigned k)

    if (n == 0 && k == 0) 
        return 1;
    

    if (n == 0 || k == 0) 
        return 0;
    

    static auto memo = std::map<std::pair<unsigned, unsigned>, mp::cpp_int>();
    auto nKPair = std::pair<unsigned, unsigned>(n, k);

    if (memo.count(nKPair) > 0) 
        return memo[nKPair];
    

    auto val = k * stirlingS2(n - 1, k) + stirlingS2(n - 1, k - 1);

    memo[nKPair] = val;

    return val;

不幸的是,当这段代码运行时,它会出现段错误。插入memo 的前 87795 个值似乎运行良好,但此后不久就崩溃了。具体来说,段错误发生在map::count,在if (memo.count(nKPair) &gt; 0) 行中。我想这可能是memo 的大小不足的问题,所以我在memo 的分配中添加了以下警告,

if (memo.size() < memo.max_size()) 
    memo[nKPair] = val;

但这并没有帮助。我还注意到 87795 值并不表示何时崩溃。通过一些小的修改,将第一个 if 语句更改为,

if (n <= k) 
    return 1;

将该值更改为 66453。

有人知道这里发生了什么吗?

【问题讨论】:

你打算在哪里插入一些东西到备忘录中? 你确定段错误在memo.count 行上,而不是由于没有正确记忆而导致通过深度递归炸毁堆栈? @DavidThomas,如果键不存在,operator[] 不会为键插入新元素吗? @JordyDickinson 是的,确实如此......但是地图中的所有内容都是默认构造的 cpp_int。这是故意的吗? 你为什么用if (memo.count(nKPair) &gt; 0) return memo[nKPair]; 而不是auto it = memo.find(nKPair); if (it != memo.end()) return it.second;?你在哪里插入地图? 【参考方案1】:

好的,经过数小时的困惑,我将其范围缩小到表达式模板问题。我不太明白为什么,但这一切都与行中的那个小 auto 有关

auto val = k * stirlingS2(n - 1, k) + stirlingS2(n - 1, k - 1)

基本上,将 auto 更改为 mp::cpp_int,然后突然就没有段错误了。

【讨论】:

以上是关于为啥这个记忆代码段错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在此代码中收到 SIGABRT 错误 [关闭]

为啥这个非常简单的构造函数会导致段错误?

为啥这段代码在 64 位架构上会出现段错误,但在 32 位上却能正常工作?

我不明白为啥这段代码不起作用 for int 错误 [重复]

为啥我的代码在 Windows 7 上不会出现段错误?

为啥这段代码在 leetcode 运行良好,但在 geeksforgeeks 出现分段错误?