尝试将单词插入trie时出现分段错误

时间:2017-02-10 13:39:32

标签: c++ pointers trie

嗨:)有谁能告诉我为什么下面的代码不起作用?程序在与if(children[word[letter_no] - 'A'] == nullptr)对应的节点中的'B'行崩溃。但是节点是创建的,当我尝试在构造函数中调用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;
}

3 个答案:

答案 0 :(得分:6)

启用编译器警告!

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

    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

来自评论:

  

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

答案 1 :(得分:2)

Vittorio已经回答了关于风格的几个词:

你只能有一种方法:

std::unique_ptr

然后您不需要覆盖,您可以使用 if (letter_no < word.length()) { auto &child = children[word[letter_no] - 'A']; if ( !child ) child = std::make_unique<Node>(); child->insert(word, ++letter_no); } 并且您不需要在您的ctor中循环,并且如果您消除了代码重复:

f

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

答案 2 :(得分:0)

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

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

考虑1 st 递归时,letter_no0word包含'A''B''C''\0'。因此letter_no会为'A'编制索引。

验证letter_noword letter_no < word.length()的有效索引后,您递增 letter_nochildren[word[letter_no] - 'A']->insert(word, ++letter_no);

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

就解决方案而言,您并不关心维护letter_no的价值,因此请执行以下操作:children[word[letter_no] - 'A']->insert(word, letter_no + 1);