CS50 Pset5 Speller程序Valgrind检测失败,存内存泄漏与指针错误
CS50x Pset5 Speller:Valgrind检测失败问题排查
正在完成CS50x课程的Pset5 Speller作业,代码功能正常,但check50的Valgrind检测不通过。用help50 Valgrind测试dictionaries/small字典时无内存泄漏,推测未调用fclose()关闭文件是原因之一,但添加该语句会触发munmap_chunk(): invalid pointer错误,附上代码和错误日志寻求问题定位。
代码
// Implements a dictionary's functionality #include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <string.h> #include <strings.h> #include <stdlib.h> #include "dictionary.h" // Represents a node in a hash table typedef struct node { char word[LENGTH + 1]; struct node *next; } node; // Number of bucket for Hash Table const unsigned int N = 676; // Hash table node *table[N]; int word_counter = 0; // Returns true if word is in dictionary, else false bool check(const char *word) { int index = hash(word); node *cursor = table[index]; while (cursor != NULL) { if (strcasecmp(word, cursor->word) == 0) { return true; } cursor = cursor->next; } return false; } // Hashes word to a number unsigned int hash(const char *word) { unsigned int index = 0; for (int i = 0; i < 26; i++) { if (word[0] == i + 'a' || word[0] == i + 'A') { index += i * 26; } if (word[1] == i + 'a' || word[1] == i + 'A') { index += i; } } return index; } // Loads dictionary into memory, returning true if successful, else false bool load(const char *dictionary) { FILE *dict_file = fopen(dictionary, "r"); if (dict_file == NULL) { return false; } table_clear(); // Use fscanf() to look at each string in the dictionary and store to buffer char *buffer = malloc(sizeof(LENGTH + 1)); if (buffer == NULL) { return false; } while (fscanf(dict_file, "%s", buffer) != EOF) { // Create a new memory space for each dictionary word, and store it here node *n = malloc(sizeof(node)); if (n == NULL) { free(buffer); return false; } // Copy each word from buffer to the location (*dict_entry).word strcpy(n->word, buffer); n->next = NULL; // Use hash function to find the hash value of the dictionary word int index = hash(n->word); if (table[index] != NULL) { n->next = table[index]; } table[index] = n; n = NULL; free(n); word_counter++; } //fclose(dict_file); <----- If activate, this line causes the munmap_chunk: invalid pointer error free(buffer); return true; } // Returns number of words in dictionary if loaded, else 0 if not yet loaded unsigned int size(void) { return word_counter; } // Unloads dictionary from memory, returning true if successful, else false bool unload(void) { for (int i = 0; i < N; i++) { node *cursor = table[i]; while (cursor != NULL) { cursor = cursor->next; free(table[i]); table[i] = cursor; } free(cursor); } return true; } // Sets every value in table to NULL void table_clear(void) { for (int i = 0; i < N; i++) { table[i] = NULL; } return; }
错误日志
**Cause valgrind tests failed; see log for more information LOG: running valgrind --show-leak-kinds=all --xml=yes --xml-file=/tmp/tmpr2rknwok -- ./speller substring/dict substring/text... checking for output "MISSPELLED WORDS ca cats caterpill caterpillars WORDS MISSPELLED: 4 WORDS IN DICTIONARY: 2 WORDS IN TEXT: 6 "... checking that program exited with status 0... checking for valgrind errors... Invalid write of size 1: (file: dictionary.c, line: 88) Invalid read of size 1: (file: dictionary.c, line: 99) 472 bytes in 1 blocks are still reachable in loss record 1 of 1: (file: dictionary.c, line: 74)
问题定位与修复
1. 缓冲区溢出(核心问题)
load函数中buffer的内存分配错误:
char *buffer = malloc(sizeof(LENGTH + 1));
sizeof(LENGTH +1)计算的是整数的字节数(通常为4或8),而非LENGTH+1个字符的空间,导致缓冲区过小,读取长单词时触发堆内存溢出,破坏内存结构。这也是添加fclose()后触发munmap_chunk()错误的根源。
修复:改为正确的内存分配方式:
char *buffer = malloc(LENGTH + 1);
2. 错误的节点内存释放
load函数中,将节点n加入哈希表后,执行了n = NULL; free(n);——这两行完全多余,且若后续逻辑出错会导致悬空指针。直接删除这两行即可。
3. 文件句柄未关闭
修复缓冲区溢出后,在load函数的free(buffer);之前添加fclose(dict_file);,即可正常关闭文件,解决Valgrind中“still reachable”的内存泄漏提示。
4. Unload函数的冗余操作
unload函数循环结束后执行free(cursor);,此时cursor已为NULL,无实际意义,直接删除该行。
5. 哈希函数的优化(可选)
原哈希函数会访问单词的第二个字符,若单词长度为1,虽不会崩溃,但可优化为仅处理存在的字符,避免无效判断:
unsigned int hash(const char *word) { unsigned int index = 0; // 处理第一个字符 if (isalpha(word[0])) { char c = tolower(word[0]); index += (c - 'a') * 26; } // 处理第二个字符(如果存在) if (isalpha(word[1])) { char c = tolower(word[1]); index += (c - 'a'); } return index; }
内容的提问来源于stack exchange,提问作者Zach Anderson
相关产品推荐
相关产品推荐

