CS50 speller.c加载函数中哈希表引发段错误求助
解决CS50 Speller作业中Load函数的段错误问题
核心错误原因
你遇到的段错误是因为全局哈希表数组table的元素未初始化就被解引用。全局指针数组默认初始化为NULL,但你的代码在load函数中直接执行table[hashnum]->next == NULL——对NULL指针进行->操作,直接触发内存访问错误(段错误)。
分步修复方案
1. 修正哈希表节点插入逻辑
把table的每个元素作为链表的头指针(而非指向一个头节点),插入节点时直接操作头指针:
// 替换原有的插入逻辑 hashnum = hash(dictword->word); // 如果当前哈希桶为空,直接让头指针指向新节点 if (table[hashnum] == NULL) { table[hashnum] = dictword; dictword->next = NULL; } // 否则把新节点插入链表头部(头插法) else { dictword->next = table[hashnum]; table[hashnum] = dictword; }
2. 修复单词读取的字符串终止问题
你的代码读取单词时没有添加字符串终止符'\0',会导致后续hash、strcasecmp操作读取垃圾数据甚至越界。修改读取单词的逻辑:
// 替换原有的单词读取循环 int tmpctr = 0; char c; // 读取字符直到换行、文件结束,或达到单词长度上限 while (fread(&c, sizeof(char), 1, dict) && c != '\n' && tmpctr < LENGTH) { dictword->word[tmpctr++] = c; } // 添加字符串终止符,确保是合法C字符串 dictword->word[tmpctr] = '\0';
3. 修正其他函数的指针访问问题
check函数:原代码直接访问table[hashnum]->next,同样会在哈希桶为空时触发段错误,改为从表头开始遍历:
bool check(const char *word) { int hashnum = hash(word); node *tmpnode = table[hashnum]; // 直接指向表头 while (tmpnode != NULL) { if (strcasecmp(word, tmpnode->word) == 0) { return true; } tmpnode = tmpnode->next; } return false; }
unload函数:原代码跳过了表头节点,改为从表头开始释放整个链表:
bool unload(void) { for(int i = 0; i < N; i++) { freehash(table[i]); } return true; } void freehash(node* node) { if (node == NULL) return; // 空链表直接返回 freehash(node->next); free(node); }
4. 修复load函数的其他小问题
- 当
fopen失败时,不要调用fclose(dict)(dict是NULL,fclose(NULL)是未定义行为):
if (dict == NULL) { printf("\n\n Could not load dictionary.\n"); return false; }
malloc失败时,无需free(dictword)(dictword已经是NULL):
node *dictword = malloc(sizeof(node)); if (dictword == NULL) { fclose(dict); return false; }
额外优化建议
- 你的哈希函数仅根据首字母分配桶,会导致大量哈希冲突,建议优化哈希逻辑(比如利用整个单词的字符计算哈希值),提升查询效率。
- 可以在程序启动时显式初始化
table数组为NULL(虽然全局数组默认初始化,但显式初始化更清晰):
// 在load函数开头添加 for (int i = 0; i < N; i++) { table[i] = NULL; }
内容的提问来源于stack exchange,提问作者HubbleMicroscope
相关产品推荐
相关产品推荐

