【问题标题】:Segmentation fault while trying to insert a word into trie尝试将单词插入 trie 时出现分段错误
【发布时间】:2017-06-28 21:13:06
【问题描述】:

嗨 :) 谁能告诉我为什么下面的代码不起作用?程序在与'B' 对应的节点中的if(children[word[letter_no] - 'A'] == nullptr) 行崩溃。但是节点 创建的,当我尝试在构造函数中调用 children[1] 时,它可以工作。但是当它在insert() 函数中被调用时,它不会...

包括

#include <memory> //shared_ptr
#include <string>    
using namespace std;    
const int ALPHABET = 26;

class Node {
public:
    shared_ptr<Node> children[ALPHABET];
    
    Node() { for (int i = 0; i < ALPHABET; ++i) children[i] = nullptr;}
    void insert(const string &word, unsigned letter_no) {
        if (letter_no < word.length()) {
            if (children[word[letter_no] - 'A'] == nullptr) 
                children[word[letter_no] - 'A'] = make_shared<Node>();
            children[word[letter_no] - 'A']->insert(word, letter_no+1);
        }
    }
};

int main() {
    Node trie{};
    trie.insert("ABC", 0);
    return 0;
}

【问题讨论】:

  • 请注意,字母不像数字那样必须在连续范围内。例如,如果系统使用 EBCDIC(它可以),那么这将不起作用。
  • 离题,但空行和括号是免费的!

标签: c++ pointers trie


【解决方案1】:

启用编译器警告!

  • 由于未指定的评估顺序,您得到未定义的行为

    children[word[letter_no] - 'A']->insert(word, ++letter_no);
    

    警告:未排序的修改和访问letter_no [-Wunsequenced]

  • 这里还有一个潜在危险的比较:

    letter_no < word.length
    

    警告:有符号和无符号整数表达式之间的比较

on wandbox


此外,您不应在现代 C++ 代码中使用 newdelete。根据您需要的所有权语义,使用 std::unique_ptrstd::shared_ptr


来自cmets:

Jecke:这都是真的,但没有一个是导致问题的原因。我简化了我的代码,以便在问题中更具可读性。在我的原始代码中,我尝试使用 shared_ptr,但结果是一样的。看,pastebin.com/MFZdrp22 没有更好的工作(仍然分段错误)

仔细看看这些行:

if (letter_no < word.length()) 
{
    if (children[word[letter_no] - 'A'] == nullptr)
    {
        children[word[letter_no] - 'A'] = make_shared<Node>();
    }

    ++letter_no;                                              // (0)
    children[word[letter_no] - 'A']->insert(word, letter_no); // (1)
}
  • word"ABC"

  • word[letter_no] - 'A'0

  • (0) 处,增加 letter_no

  • (1) 处,word[letter_no] - 'A'1

  • children[1]nullptr轰隆隆!

同样,编译器是你的朋友。使用-fsanitize=undefined 编译会得到如下错误信息:

runtime error: member call on null pointer of type 'Node'
runtime error: member access within null pointer of type 'Node'

on wandbox

【讨论】:

  • 如果OP消除了代码重复问题就会消失
  • 没错,但没有一个是导致问题的原因。我简化了我的代码,以便在问题中更具可读性。在我的原始代码中,我尝试使用 shared_ptr,但结果是一样的。看,pastebin.com/MFZdrp22 没有更好的工作(仍然分段错误)
  • @Jecke:仔细阅读第 18 行和第 19 行。您正在访问nullptr!你可能想要的是children[word[letter_no] - 'A']-&gt;insert(word, letter_no + 1);
  • @Jecke:如果您觉得我的回答解决了您的问题,您应该将其标记为已接受
【解决方案2】:

Vittorio 已经回答了关于风格的原因:

你只能有一种方法:

void insert(const string &word, size_t letter_no = 0);

那么你不需要覆盖,你可以使用std::unique_ptr,你的ctor中不需要循环,如果你消除了代码重复:

    if (letter_no < word.length()) {
        auto &child = children[word[letter_no] - 'A'];
        if ( !child ) 
            child = std::make_unique<Node>();
        child->insert(word, ++letter_no);
    }

这不仅会使您的代码更具可读性,而且会让您的问题消失

【讨论】:

    【解决方案3】:

    Vittorio Romeo's answer 是正确的。你应该经常清理你的警告。

    但为了给你一个完整的解释:

    考虑当您的第一个st 递归时,letter_no0word 包含 'A''B''C''\0'。所以letter_no 索引'A'

    在验证letter_noword 的有效索引后:letter_no &lt; word.length()递增 letter_nochildren[word[letter_no] - 'A']-&gt;insert(word, ++letter_no);

    letter_no 作为该行上的第 1st 操作递增,因此它实际上具有值 1,索引 'B'。然后减去'A',得到1 的索引,这是一个未分配的元素。


    就解决方案而言,您并不关心维护letter_no 的价值,所以只需这样做:children[word[letter_no] - 'A']-&gt;insert(word, letter_no + 1);

    【讨论】:

      猜你喜欢
      • 2020-06-01
      • 2015-08-27
      • 1970-01-01
      • 2013-12-14
      • 2023-03-11
      • 1970-01-01
      • 2014-07-27
      • 2014-11-21
      • 1970-01-01
      相关资源
      最近更新 更多