CS50 Pset5,在大词典上运行良好,但在小词典上出现段错误

CS50 Pset5, runs fine with large dictionary but seg faults on small dictionary

我在使用 CS50 课程的 PSET 5 时遇到一些问题。这是拼写问题。当我尝试加载小词典时,我的代码出现段错误。

用 gdb 调试后,显示错误发生在我试图在第 83 行的 fopen(dictionary, "r") 时。

bool load(const char *dictionary)
{
    // fopen dictionary file (remb to check if NULL)
    FILE *dict = fopen(dictionary, "r");
    if (dict == NULL)
    {
        printf("Could not open file\n");
        return false;
    }

它returns“无法打开file\n”)。但是我不明白为什么会这样,有人可以帮忙吗?大型词典的所有内容似乎都 运行 没问题。

完整代码:

// Implements a dictionary's functionality
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include "dictionary.h"

// Represents a node in a hash table
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
}
node;

// Number of buckets in hash table
const unsigned int N = 256;

// Hash table
node *table[N];

// Word_count for size function
unsigned long word_count = 0;

// Returns true if word is in dictionary else false
bool check(const char *word)
{
    char small_word[LENGTH+1];
    for (int i = 0; i < LENGTH + 1; i++)
    {
        small_word[i] = 0;
    }
    
    for (int i = 0, n = strlen(word); i < n; i++)
    {
        if(isupper(word[i]))
        {
            small_word[i] = tolower(word[i]);
        }
        else
        {
            small_word[i] = word[i];
        }
    }
    // hash the word to get hash value
    unsigned long hvalue = hash(small_word);

    // point cursor to the first value in the table with index of hvalue
    node* cursor = table[hvalue];

    // as long as the strings don't match, we reassign cursor to the next value
    while (strcasecmp(small_word, cursor->word) != 0)
    {
        cursor = cursor->next;
        // since strings dont match, if they reach to the end where next is NULL
        if (cursor == NULL)
        {
            // then the word is misspelled
            return false;
        }
    }
    return true;
}

// Hashes word to a number
unsigned long hash(const char *word)
{
    // djb2 hash function as seen from http://www.cse.yorku.ca/~oz/hash.html
    unsigned long hash = 0;
    int c;

    while ((c = *word++))
    {
        hash = c + (hash << 6) + (hash < 16) - hash;
    }
    return (hash % N);
}

// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
    // fopen dictionary file (remb to check if NULL)
    FILE *dict = fopen(dictionary, "r");
    if (dict == NULL)
    {
        printf("Could not open file\n");
        return false;
    }

    char storage[LENGTH + 1];
    for (int i = 0; i < LENGTH + 1; i++)
    {
        storage[i] = 0;
    }

    while (fscanf(dict, "%s", storage) != EOF)
    {
        // allocate space for buffer node
        node *buffer = malloc(sizeof(node));
        if (buffer == NULL)
        {
            printf("Error allocating memory\n");
            return 1;
        }
        // copy the string in storage into the buffer node
        strcpy(buffer->word, storage);

        // hash the word to obtain hvalue
        unsigned long hvalue = hash(buffer->word);
        
        // assigns buffer's next to point to the current words inside the table
        buffer->next = table[hvalue];
        
        // after making sure that buffer next keeps the address of the other words, you assign the table hvalue to the most recent word
        table[hvalue] = buffer;
        
        // increase word count by one
        word_count++;
        
    }

    fclose(dict);
    return true;
}

// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned long size(void)
{
    return word_count;
}

// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
    word_count = 0;

    for (int i = 0; i < N; i++)
    {
        node *cursor = table[i];
        while (cursor != NULL)
        {
            node *tmp = cursor;
            cursor = cursor->next;
            free(tmp);
        }
        
        free(cursor);
    }
        
    return true;
}

“无法打开文件”消息可能是命令行上的错字(如评论中所述,它不会产生段错误,因为它给出了消息,然后 returns false 给拼写者,拼写者给出消息并退出)。

尝试 debug50,并在 node* cursor = table[hvalue]; 处设置断点进行检查。使用 texts/cat.txt 作为文本文件。一旦进入下一行,程序很可能会出现段错误。

小词典(随发行代码一起提供)不会填充 table 的所有成员。如果 table[index] 未填充,则不存在 table[index]->nexttable[index]->word 等内容。因此,这个 while (strcasecmp(small_word, cursor->word) != 0) 是一个等待发生的段错误。