[英]Segmentation fault while trying to insert a word into trie
嗨:)誰能告訴我下面的代碼為什么不起作用? 程序在與'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;
}
啟用您的編譯器警告!
由於未指定評估順序,因此出現未定義的行為 :
children[word[letter_no] - 'A']->insert(word, ++letter_no);
警告:未進行
letter_no
修改和訪問letter_no
[-未進行順序]
您在這里也有一個潛在的危險比較:
letter_no < word.length
警告:有符號和無符號整數表達式之間的比較
同樣,您不應該在現代C ++代碼中使用new
和delete
。 根據所需的所有權語義,使用std::unique_ptr
或std::shared_ptr
。
從評論:
傑克 :沒錯,但這都不是造成問題的原因。 我簡化了代碼,使它在一個問題中更具可讀性。 在我的原始代碼中,我嘗試使用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'
維托里奧(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);
}
這不僅會使您的代碼更具可讀性,而且會使您的問題消失
維托里奧·羅密歐的答案是正確的。 您應該始終清理警告。
但是為了給您一個完整的解釋:
考慮一下您的第一個遞歸, letter_no
為0
。 word
包含'A'
, 'B'
, 'C'
和'\\0'
。 所以letter_no
為'A'
索引。
確認letter_no
是word
的有效索引后: letter_no < word.length()
您可以遞增 letter_no
: children[word[letter_no] - 'A']->insert(word, ++letter_no);
letter_no
作為此行的第一個操作遞增,因此它實際上具有值1
,索引為'B'
。 然后將其減去以'A'
得出的索引為1
,這是未分配的元素。
就解決方案而言,您不必關心保持letter_no
的值,因此只需執行以下操作: children[word[letter_no] - 'A']->insert(word, letter_no + 1);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.