【问题标题】:Segmentation fault caused after iterating through a queue. Possibly caused by unique_ptr?遍历队列后导致的分段错误。可能是由 unique_ptr 引起的?
【发布时间】:2021-12-11 03:14:49
【问题描述】:

我在运行自动完成功能时不断收到段错误。

gdb 给出以下段错误错误:

Program received signal SIGSEGV, Segmentation fault.
std::__uniq_ptr_impl<cs19::CompactStringSet::Node, std::default_delete<cs19::CompactStringSet::Node> >::_M_ptr (this=0x8) at /usr/include/c++/11/bits/unique_ptr.h:173
173           pointer    _M_ptr() const { return std::get<0>(_M_t); }

要发布的内容很多,要查看的代码的主要部分就在最后的 // step 3 注释之后。我将其余部分主要用于上下文。

在我的函数中std::get&lt;0&gt; 是一个指针。段错误可能是因为pop(),尽管在gdb 中它在获得段错误之前对一个元素执行了pop()。我正在使用我不太熟悉的unique_ptrs,我知道它们应该自动删除,但我可能在实现中出错。错误在while(sibling-&gt;sibling)

该函数预计会遍历以child/sibling 方式链接的字符数据集,并返回最快匹配的std::vector,但不超过n

我知道 seg 故障不是一成不变的,所以我不期待一个确切的答案,任何帮助!

std::vector<std::string> CompactStringSet::autocomplete(const std::string& base, std::size_t n) const {    
    // check children, if no match -> check siblings, if no match -> return empty vector; after
    // all matches to base, check all base->child->sibling then check each ->child
    // each option, add any which evaluate to terminal. Exit after n have been found,
    // or no options remain
    std::vector<std::string> results;
    std::queue<std::pair<const Node*, std::string>> matches;
    const Node* cur = &this->root_;
    std::size_t numResults = 0;
    // step 1 : READ IN BASE, MAKE SURE IT'S VALID
    for (auto let : base) {
        if (cur->child) {
            if (cur->child->letter == let) {
                cur = cur->child.get();
                continue;
            }
        }
        Node* sibling = cur->child.get();
        while (sibling->sibling) {
            sibling = sibling->sibling.get();
            if (sibling->letter == let) {
                cur = sibling;
                continue;
            }
        }
        return results;
    }
    // step 2 : start from after base and check options  make sure if terminal
    //          and initialize queue
    if (cur->child) {
        std::string toAdd = base + cur->child->letter;
        auto newElem = std::make_pair(cur->child.get(), toAdd);
        matches.push(newElem);
    }
    Node* newSibling = cur->child.get();
    while (newSibling->sibling) {
        newSibling = newSibling->sibling.get();
        std::string toAdd = base + newSibling->letter;
        auto newElem = std::make_pair(newSibling, toAdd);
        matches.push(newElem);
    }
    // step 3 : check each node*, if terminal add to results
    do {
        // check first in queue
        auto elem = matches.front();
        auto curNode = std::get<0>(elem);
        auto curStr = std::get<1>(elem);
        if (std::get<0>(elem)->terminal) {
            results.push_back(std::get<1>(elem));
            ++numResults;
        }
        if (curNode->child) {
            std::string updated = curStr + curNode->child->letter;
            auto newElem = std::make_pair(curNode->child.get(), updated);
            matches.push(newElem);
        }
        Node* sibling = curNode->child.get();
        while (sibling->sibling) {                // where error message occured
            sibling = sibling->sibling.get();
            std::string updated = curStr + sibling->letter;
            auto newElem = std::make_pair(sibling, updated);
            matches.push(newElem);
        }
        matches.pop();
    } while (numResults < n && matches.size() > 0);
    return results;
}

【问题讨论】:

  • 仅仅因为这是程序崩溃或报告错误的地方并不意味着这就是问题所在。 C++ 不能以这种方式工作。问题可能出现在代码中的任何地方,但在出现错误后,程序会继续运行一段时间,然后最终崩溃。这就是为什么 stackoverflow.com 的 help center 要求您展示一个 minimal reproducible example,其他人都可以完全如图所示剪切/粘贴,然后编译、运行和重现您的问题。有关更多信息,请参阅How to Ask。在您这样做之前,任何人都不太可能找出您的问题。
  • 这段代码看起来很适合在 valgrind 下运行,以查看 valgrind 可以在段错误发生之前检测到哪些早期内存处理问题。
  • @JeremyFriesner 谢谢,我会尝试在 valgrind 下运行它

标签: c++


【解决方案1】:

我怀疑错误在这里:

    if (cur->child) {
        if (cur->child->letter == let) {
            cur = cur->child.get();
            continue;
        }
    }

    Node* sibling = cur->child.get();
    while (sibling->sibling) {

特别是,上面引用的第一行中的if (cur-&gt;child) 测试表明代码的作者知道cur-&gt;child 可能是空的unique_ptr,并且正在防范这种可能性。很公平。但就在下面,我们有这段代码将sibling 设置为等于(无论cur-&gt;child 指向什么,作为原始指针)并尝试在下一行取消引用sibling。在这种情况下,如果cur-&gt;childNULL,那么sibling 也将为NULL,因此尝试评估sibling-&gt;sibling 将导致段错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-03
    • 2013-09-16
    • 2016-03-30
    • 1970-01-01
    • 1970-01-01
    • 2019-04-26
    • 1970-01-01
    • 2018-12-09
    相关资源
    最近更新 更多