CS50 Speller项目Valgrind检测内存泄漏求助
CS50 Speller项目内存泄漏问题解决
问题概述
CS50 Speller项目代码无法通过Valgrind测试,存在内存泄漏。Valgrind日志显示有56字节的内存块仍可访问,泄漏点指向代码第96行的node *current_wrd = malloc(sizeof(node));语句。
Valgrind报错日志
running valgrind --show-leak-kinds=all --xml=yes --xml-file=/tmp/tmpx3faci2p -- ./speller substring/dict substring/text... checking for output "MISSPELLED WORDS\n\nca\ncats\ncaterpill\ncaterpillars\n\nWORDS MISSPELLED: 4\nWORDS IN DICTIONARY: 2\nWORDS IN TEXT: 6\n"... checking that program exited with status 0... checking for valgrind errors... 56 bytes in 1 blocks are still reachable in loss record 1 of 1: (file: dictionary.c, line: 96)
完整代码
// Implements a dictionary's functionality #include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <strings.h> #include <string.h> #include <stdlib.h> #include <cs50.h> #include "dictionary.h" // 全局变量 unsigned int word_count; unsigned int hash_num; // 哈希表节点结构 typedef struct node { char word[LENGTH + 1]; struct node *next; } node; // 哈希表桶数量 const unsigned int N = 26; // 哈希表 node *table[N]; // 检查单词是否在字典中 bool check(const char *word) { hash_num = hash(word); node* cursor = table[hash_num]; while(cursor != NULL) { if(strcasecmp(cursor->word, word) == 0) { return true; } cursor = cursor->next; } return false; } // 哈希函数 unsigned int hash(const char *word) { unsigned long total = 0; for(int i = 0; i < strlen(word); i++) { total+=tolower(word[i]); } return total % N; } // 加载字典到内存 bool load(const char *dictionary) { FILE *source = fopen(dictionary,"r"); if(source == NULL) { printf("Could not open the file\n"); return false; } char current_word[LENGTH+1]; while (fscanf(source, "%s", current_word) != EOF) { node *current_wrd = malloc(sizeof(node)); if(current_wrd == NULL) { return false; } strcpy(current_wrd->word, current_word); hash_num = hash(current_word); current_wrd->next = table[hash_num]; table[hash_num] = current_wrd; word_count++; } fclose(source); return true; } // 返回字典中的单词数量 unsigned int size(void) { if(word_count > 0) { return word_count; } return 0; } // 释放字典内存 bool unload(void) { for(int i = 0; i < N; i++) { node *cursor = table[i]; while(cursor) { node *tmp = cursor; cursor = cursor->next; free(tmp); } if(cursor == NULL) { free(cursor); return true; } } return false; }
问题定位
内存泄漏的根源在unload函数:
- 函数在遍历第一个桶(i=0)并释放其链表节点后,直接执行
return true,导致后续N-1个桶的节点完全没有被释放。 - 当字典中的单词分布在多个桶时,未处理的桶内节点会成为"仍可访问"的内存块,触发Valgrind报错。
修复方案
修改unload函数,遍历所有N个桶完成节点释放后,再统一返回true:
bool unload(void) { for(int i = 0; i < N; i++) { node *cursor = table[i]; while(cursor) { node *tmp = cursor; cursor = cursor->next; free(tmp); } } return true; }
修复说明
- 移除了原函数中处理完单个桶就返回的逻辑,确保所有桶的链表节点都被遍历释放。
- 无需额外
free(cursor),因为循环结束时cursor已经是NULL,free(NULL)是安全但无意义的操作。 - 遍历完所有桶后直接返回
true,因为只要进入函数就会完成所有内存释放。
内容的提问来源于stack exchange,提问作者keshika sathsara
相关产品推荐
相关产品推荐

