【问题标题】:Segmentation Fault (core dumped). In a Hash Table using separate chaining. (C++)分段错误(核心转储)。在使用单独链接的哈希表中。 (C++)
【发布时间】:2023-03-03 00:24:01
【问题描述】:

我目前正在创建一个哈希表,它使用单独的链接作为冲突解决方案。我在结构中实现了一个指向下一个节点的 next 指针,类似于链表的结构。

每当我尝试访问下一个节点的单词时,我都会收到一条内存错误,提示 Segmentation Fault (core dumped)。

cout<<hashTable[5].next->word; //this line produces the memory error

这是什么原因造成的,我该如何解决?

#include <iostream>

using namespace std;

struct Node{
    string word;
    Node* next;
    bool empty;
};


void hashInsert(Node table[], string word){
    int index = 5; //should add hashFunction() if not testing
    Node* current = &table[index];
    if(table[index].empty){
        table[index].word = word;
        table[index].empty = false;
    }
    else{
        while(current->next != nullptr){
            current = current->next;
        }
        current = new Node();
        current->word = word;
        current->next = nullptr;
        current->empty = false;
        
    }
}


int main()
{
Node hashTable[10];
hashInsert(hashTable, "test");
hashInsert(hashTable, "test");
cout<<hashTable[5].next->word;
}

【问题讨论】:

    标签: c++


    【解决方案1】:
    Node hashTable[10]; 
    

    保持其内容未初始化。这使得hashInserta bit more of an adventure 中的一些东西比你想在计算机程序中看到的要多。使用

    Node hashTable[10] = {}; 
    

    Zero Initialize 数组并获得更多可预测性。

    但这并不能解决所有问题。零初始化会将 empty 设置为 false,但事实并非如此。您需要一个默认构造函数(或成员上的 default member initializers)将值强制为 true 或反转逻辑。

    当您仔细研究这两种方法时,您会意识到empty 标志是唯一需要的,因为列表中的第一个节点可能未使用。但是如果不需要第一个节点,如果没有第一个节点怎么办? empty 的作业可以用空指针来处理。

    Node * hashTable[10] = {}; // treat each entry in the hash table like a next
                               // without the rest of the node
    

    这可以让您将hashInsert 缩小到

    void hashInsert(Node * table[], string word) {
        int index = 5; //should add hashFunction() if not testing
        Node *current = table[index]; //
        if (table[index] == nullptr)
        {
            table[index] = new Node{word, nullptr};
        }
        else
        {
            while (current->next != nullptr)
            {
                current = current->next;
            }
            current = new Node{word, nullptr};
        }
    }
    

    注意: Node{word, nullptr}; 使用aggregate initialization 允许我们创建Node 并一次性设置其所有成员。非常方便。

    但这不起作用,因为current 是一个局部作用域的自动变量,所以分配给它的新Node 在函数返回时就会丢失。我们需要更智能的东西。

    Ranoiaetep 建议 current-&gt;next = new Node();。这会起作用,但我们可以变得更聪明。

    void hashInsert(Node * table[], string word) {
        int index = 5; //should add hashFunction() if not testing
        Node **current = &table[index]; // point at the next pointer! Now we know 
                                        // the correct place to insert in ALL cases
        if (*current == nullptr)
        {
            *current = new Node{word, nullptr};
        }
        else
        {
            while (*current != nullptr)
            {
                current = &(*current)->next;
            }
            *current = new Node{word, nullptr};
        }
    }
    

    在这个版本中,您可能会注意到if (*current == nullptr)while (*current != nullptr) 并没有那么不同。在if 中我们在没有节点时添加一个新节点。在while 中,我们寻找没有节点,然后添加一个新节点。无论哪种方式,没有节点意味着添加一个节点。我们或许可以将它们结合起来。

    void hashInsert(Node * table[], string word) {
        int index = 5; //should add hashFunction() if not testing
        Node **current = &table[index]; 
        while (*current != nullptr) // look for end of list
        {
            current = &(*current)->next;
        }
        *current = new Node{word, nullptr}; // add to end of list
    }
    

    哇。 4 行代码和几个大括号。

    整体看起来像

    #include <iostream>
    
    using namespace std;
    
    struct Node {
        string word;
        Node *next;
    };
    
    void hashInsert(Node * table[], string word) {
        int index = 5; //should add hashFunction() if not testing
        Node **current = &table[index]; 
        while (*current != nullptr)
        {
            current = &(*current)->next;
        }
        *current = new Node{word, nullptr}; 
    }
    
    int main() {
        Node * hashTable[10] = {};
        hashInsert(hashTable, "test");
        hashInsert(hashTable, "test");
        cout << hashTable[5]->next->word;
    }
    

    【讨论】:

    • 您将如何访问“当前”指针的单词成员?我想添加一个相等语句,说如果参数from的单词在遍历时与当前单词相等,那么我就不需要再添加了。
    • 就像(*current)-&gt;next。您取消引用current ,将其从指向Node 的指针更改为指向Node 的指针,然后使用-&gt; 运算符访问该成员。
    【解决方案2】:

    几件事:

    1. bool empty 默认为 false,我假设您的意思正好相反。
    2. 调用后:
    while(current->next != nullptr){
        current = current->next;
    }
    

    您的current 实际上是最后一个非nullptr。因此,您随后拨打的任何电话都是针对您插入的第一个 Node 进行的。

    while 循环之后,您需要添加:

    current->next = new Node();
    current = current->next;
    

    编辑:

    不改变其他结构,代码如下:

    #include <iostream>
    
    using namespace std;
    
    struct Node{
        string word;
        Node* next;
        bool empty = true; // empty is now defaulted to true
    };
    
    
    void hashInsert(Node table[], string word){
        int index = 5;
        Node* current = &table[index];
        if(table[index].empty){
            table[index].word = word;
            table[index].empty = false;
        }
        else{
            while(current->next != nullptr){
                current = current->next;
            }
            current->next = new Node(); // A new Node is added after current
            current = current->next; // Now current is set to the next Node
            current->word = word;
            // current->next = nullptr; // This is not needed
            current->empty = false;
        }
    }
    
    
    int main()
    {
        Node hashTable[10];
        hashInsert(hashTable, "test");
        hashInsert(hashTable, "test");
        cout << hashTable[5].word << ", ";
        cout << hashTable[5].next->word;
    }
    

    【讨论】:

    • 感谢您的改进!但这仍然不能解决内存访问问题。你有什么想法可以解决这个问题吗?
    • @Jack 检查编辑是否是您最终拥有的代码。它对我来说很好。
    • @Jack 或here 直播
    • Ranoiaetep 您还需要Node* next = nullptr;while(current-&gt;next != nullptr) 测试将看到未初始化的指针并失败。这可能是@Jack 绊倒的原因。
    • 未初始化的指针就像int。如果您不设置或强制设置它,您会得到任何发生的情况。初始化它,while(current-&gt;next) 应该可以工作,但这只是因为您之前已经过测试并证明current 不为空。
    猜你喜欢
    • 2018-10-29
    • 1970-01-01
    • 1970-01-01
    • 2017-07-19
    • 1970-01-01
    • 2016-03-19
    • 1970-01-01
    • 2022-01-14
    • 2017-02-25
    相关资源
    最近更新 更多