【问题标题】:Why does my hash table program keep crashing?为什么我的哈希表程序总是崩溃?
【发布时间】:2020-07-28 14:36:41
【问题描述】:

我正在尝试创建一个程序来读取字典,然后将单词存储到哈希表中,然后读取另一个文件检查该文件的每个单词是否在哈希表中,如果不是,那么它将是输出为拼写错误的单词。我首先尝试检查是否可以将字典文件加载到我的哈希表中,然后输出哈希表中的单词,但每当我尝试运行它时,我的代码似乎就会崩溃。我使用的哈希函数取自互联网。我对数据结构还是很陌生,很难理解。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// file to read
#define dictionary "dictionary.txt"
// No. of buckets
const unsigned int N = 10;

typedef struct node
{
    char* word;
    struct node *next;
}
node;

node *table[10];

// hash function
unsigned int hash(char *word)
{
// TODO
    unsigned int hash = 5381;
    int c = 0;

    while (c == *word++)
        hash = ((hash << 5) + hash) + c;

    return hash % 10;
}

int main(void)
{
    // initialize array heads to NULL
    for (int i = 0; i < N; i++)
    {
        table[i] = NULL;
    }

    // Open file to read
    FILE *indata = fopen(dictionary, "r");   
    if (indata == NULL)
    {
        printf("cant open\n");
        return 1;
    }

    // variable to store words read from the file
    char *words = malloc(sizeof(char) * 20);
    if (words == NULL)
    {
        printf("no memory\n");
        return 1;
    }

    // While loop to read through the file
    while (fgets(words, 20, indata))
    {
        // get the index of the word using hash function
        int index = hash(words);

        // create new node
        node *newNode = malloc(sizeof(node));
        if (newNode == NULL)
        {
            printf("here\n");
            return 1;
        }

        // make the new node the new head of the list
        strcpy(newNode->word, words);
        newNode->next = table[index];
        table[index] = newNode;

        // free memory
        free(newNode);
    }
    // free memory
    free(words);
    // loop to print out the values of the hash table
    for (int i = 0; i < N; i++)
    {
        node *tmp = table[i];
        while (tmp->next != NULL)
        {
            printf("%s\n", tmp->word);
            tmp = tmp->next;
        }
    }

    // loop to free all memory of the hash table
    for (int i = 0; i < N; i++)
    {
        if (table[i] != NULL)
        {
            node *tmp = table[i]->next;
            free(table[i]);
            table[i] = tmp;
        }
    }

    // close the file
    fclose(indata);
}

【问题讨论】:

标签: c hash hashtable


【解决方案1】:

粗略一看,我发现了两个问题:

  1. 您没有为节点中的单词分配空间;你只需将strcopy 这个词变成一个未定义的指针。您可能想改用strdup

  2. 在将节点添加到列表后释放它的内存。该表是一个指针数组,因此您将点存储在表中,然后丢弃它指向的内存。

哦,三:在最后一个循环中,你再次释放未分配的内存......

【讨论】:

  • strcpy(newNode-&gt;word, words); 你说的是1.中的这行代码吗??我想当你使用node *newNode = malloc(sizeof(node)); newNode 时,将为char *word; and struct node *next; 分配内存
  • 我尝试使用 print 语句来定位错误,我的程序在执行 while (fgets(words, 20, indata)) 的 1 个循环后似乎崩溃了,然后在获取下一个单词的索引之后似乎在下一个循环中崩溃了..
  • @OjouNii malloc指针 获取内存,而不是内容。您的第二条评论与第二个错误一致。
【解决方案2】:

至少三个独立导致段错误的错误:

首先,newNode-&gt;word 被使用单元化,所以它指向随机内存,所以strcpy 会出现段错误。最好使用strdup

此外,在您将newNode 放入表中后,您执行free(newNode) 使其指向的内容无效。这会导致第二个循环出现段错误

第三,在第二个循环中,如果table[i]为null,则while (tmp-&gt;next != NULL)会出现段错误

我已经注释并更正了您的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// file to read
#define dictionary "dictionary.txt"

// No. of buckets
const unsigned int N = 10;

typedef struct node {
    char *word;
    struct node *next;
} node;

node *table[10];

// hash function
unsigned int
hash(char *word)
{
// TODO
    unsigned int hash = 5381;
    int c = 0;

    while (c == *word++)
        hash = ((hash << 5) + hash) + c;

// NOTE: not a bug but probably better
#if 0
    return hash % 10;
#else
    return hash % N;
#endif
}

int
main(void)
{
    // initialize array heads to NULL
    for (int i = 0; i < N; i++) {
        table[i] = NULL;
    }

    // Open file to read
    FILE *indata = fopen(dictionary, "r");

    if (indata == NULL) {
        printf("cant open\n");
        return 1;
    }

    // variable to store words read from the file
    char *words = malloc(sizeof(char) * 20);

    if (words == NULL) {
        printf("no memory\n");
        return 1;
    }

    // While loop to read through the file
    while (fgets(words, 20, indata)) {
        // get the index of the word using hash function
        int index = hash(words);

        // create new node
        node *newNode = malloc(sizeof(node));

        if (newNode == NULL) {
            printf("here\n");
            return 1;
        }

        // make the new node the new head of the list
// NOTE/BUG: word is never set to anything valid -- possible segfault here
#if 0
        strcpy(newNode->word, words);
#else
        newNode->word = strdup(words);
#endif
        newNode->next = table[index];
        table[index] = newNode;

        // free memory
// NOTE/BUG: this will cause the _next_ loop to segfault -- don't deallocate
// the node you just added to the table
#if 0
        free(newNode);
#endif
    }

    // free memory
    free(words);

    // loop to print out the values of the hash table
    for (int i = 0; i < N; i++) {
        node *tmp = table[i];
// NOTE/BUG: this test fails if the tmp is originally NULL (i.e. no entries
// in the given hash index)
#if 0
        while (tmp->next != NULL) {
#else
        while (tmp != NULL) {
#endif
            printf("%s\n", tmp->word);
            tmp = tmp->next;
        }
    }

    // loop to free all memory of the hash table
    for (int i = 0; i < N; i++) {
        if (table[i] != NULL) {
            node *tmp = table[i]->next;

            free(table[i]);
            table[i] = tmp;
        }
    }

    // close the file
    fclose(indata);
}

更新:

我之前做了一个链表程序,在列表中存储一个整数,int number; struct node *next;,我使用了newNode-&gt;number = 5;,它工作了,为什么在这种情况下它没有?是因为我在这里处理字符串吗??

区别在于word 是一个指针。必须先为其分配一个值,然后才能使用它。 strcpyword赋值。它尝试使用 word 的内容作为副本的目标地址。

但是,无论 wordchar * 还是 numberint,都会发生其他两个错误。

如果您将word 不是定义为指针,而是作为固定数组[在这种用法中效果不佳],strcpy 会起作用.也就是说,如果您已经完成(例如)char word[5];

,而不是 char *word;

但是,除非您可以保证word 的长度可以容纳输入,否则您所做的会更好[使用strdup 更改]。 strdup 将保证这一点。

但是,请注意我 [故意] 使 word 只有五个字符来说明问题。这意味着要添加的单词长度只能是 4 个字符[我们需要一个额外的字节用于 nul 终止符]。您需要使用strncpy 而不是strcpy,但strncpy 有问题[如果源长度太大,它确实保证在末尾添加 nul 字符]。

巧合的是,今天还有一个问题的答案可能有助于进一步了解您的 word 结构成员的差异:Difference between memory allocations of struct member (pointer vs. array) in C

【讨论】:

  • 我之前做了一个链表程序,在列表中存储了一个整数,int number; struct node *next;,我使用了newNode-&gt;number = 5;,它工作了,为什么在这种情况下它没有?是因为我在这里处理字符串吗??
  • 另外,我实际上正在学习 CS50x 课程,这是针对问题集的,我在在线 ide 上使用了 strdup() 函数,但出现错误 `implicit declaration of function 'strdup' is invalid in C99` 我已经包含了 tho...
  • 我已对我的答案添加了更新,以帮助澄清您的第一条评论。但是,在某种程度上,我很惊讶您可以使用strcpy 没有 #include,但需要它用于strdup。这两个函数都在string.h 中定义。我的最佳猜测:我稍微熟悉cs50,因为这里有其他海报。它让您包含一个自定义的.h 文件。该文件可以定义strcpy,但定义strdup。我一直在寻找cs50 源文件的可下载版本,但他们现在似乎只进行基于 Web 的开发?
猜你喜欢
  • 1970-01-01
  • 2013-12-01
  • 2011-05-31
  • 2021-03-14
  • 1970-01-01
  • 2011-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多