我正在编写一个代码,该代码应该加载字典并检查文本中的单词是否正确。
代码在使用小列表(用于字典)时进行编译,但当我使用大列表运行它时,我收到“分段错误:核心转储”。
我用 valgrind 检查是否存在内存泄漏,显然与我定义 tmp 的位置一致。 (节点 *tmp = malloc(sizeof(节点)))。
我尝试通过在不同的代码行中添加 free(tmp) 来为节点 *tmp 添加释放内存来修复错误。但最终的结果大多是字典加载不正确。
以下是代码。
// Implements a dictionary's functionality
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cs50.h>
#include <strings.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
} node;
// TODO: Choose number of buckets in hash table
const unsigned int N = 26;
// Hash table
node *table[N];
// Returns true if word is in dictionary, else false
bool check(const char *word) // this is from another file which works fine.
{
int hash_value = hash(word);
node *table_tmp = table[hash_value];
for(node *ptr = table_tmp; ptr != NULL; ptr = ptr->next)
{
if (strcasecmp(ptr->word, word))
{
return true;
}
}
return false;
}
// Hashes word to a number
unsigned int hash(const char word[LENGTH + 1])
{
return toupper(word[0]) - 'A';
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// Opens the dictionary
FILE *source = fopen(dictionary, "r");
if (source == NULL)
{
fclose(source);
return 0;
}
char word_read[LENGTH + 1];
// Reads data
while (fgets(word_read, sizeof(word_read), source) != NULL)
{
unsigned int hash_value = hash(word_read);
node *tmp = malloc(sizeof(node));
if (tmp == NULL)
{
return 1;
}
strcpy(tmp->word, word_read);
tmp->next = NULL;
// Appends with tmp if table is empty
if (table[hash_value] == NULL)
{
table[hash_value] = tmp;
}
// Appends with tmp itaretively using ptr pointer
else
{
for(node *ptr = table[hash_value]; ptr != NULL; ptr = ptr->next)
{
if (ptr->next == NULL)
{
ptr->next = tmp;
break;
}
}
}
for(node *ptr = tmp; ptr != NULL; ptr = ptr->next)
{
if (ptr->next == NULL)
{
break;
}
free(ptr);
ptr = NULL;
}
}
if (size() != 0)
{
return true;
}
else
{
return false;
}
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
int counter = 0;
for(int i = 0; i < N; i++)
{
for(node *ptr = table[i]; ptr != NULL; ptr = ptr->next)
{
counter++;
if (ptr->next == NULL)
{
break;
}
}
}
if (counter == 0)
{
return 0;
}
return counter;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
for(int i = 0; i < N; i++)
{
for(node *ptr = table[i]; ptr != NULL; ptr = ptr->next)
{
if (ptr->next == NULL)
{
break;
}
free(ptr);
}
}
for(int i = 0; i < N; i++)
{
if (table[i] != NULL)
{
table[i] = NULL;
}
}
for(int i = 0; i < N; i++)
{
if (table[i] != NULL)
{
return false;
}
}
return true;
}
当我运行 valgrind 检查时,以下是输出。
很明显,内存导致的原因是未释放节点 *tmp,但我需要有关如何执行此操作的帮助。
另外,我很感谢有关 for 循环实现的任何反馈(也许这就是内存问题背后的根本原因)。
`==10230==
==10230== HEAP SUMMARY:
==10230== in use at exit: 696 bytes in 5 blocks
==10230== total heap usage: 12 allocs, 7 frees, 10,552 bytes allocated
==10230==
==10230== 224 bytes in 4 blocks are definitely lost in loss record 1 of 2
==10230== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==10230== by 0x109A36: load (dictionary.c:67)
==10230== by 0x1092CB: main (speller.c:40)
==10230==
==10230== 472 bytes in 1 blocks are still reachable in loss record 2 of 2
==10230== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==10230== by 0x49CEE6E: __fopen_internal (iofopen.c:65)
==10230== by 0x49CEE6E: fopen@@GLIBC_2.2.5 (iofopen.c:86)
==10230== by 0x1099DE: load (dictionary.c:52)
==10230== by 0x1092CB: main (speller.c:40)
==10230==
==10230== LEAK SUMMARY:
==10230== definitely lost: 224 bytes in 4 blocks
==10230== indirectly lost: 0 bytes in 0 blocks
==10230== possibly lost: 0 bytes in 0 blocks
==10230== still reachable: 472 bytes in 1 blocks
==10230== suppressed: 0 bytes in 0 blocks
==10230==
==10230== For lists of detected and suppressed errors, rerun with: -s
==10230== ERROR SUMMARY: 4 errors from 2 contexts (suppressed: 0 from 0)`
代码至少存在这些问题。 如果全部修复,可能/可能不会解决OP的困境。
指针错误
代码尝试在
ptr->next
之后访问
free(ptr);
// vvvvvvvvv ---> bad
for(node *ptr = table[i]; ptr != NULL; ptr = ptr->next) {
if (ptr->next == NULL) {
break;
}
free(ptr);
}
为什么要保留
'\n'
?
最好在
'\n'
结束时砍掉潜力word_read[]
。
word_read[strcspn(word_read, "\n")] = '\0';
潜在无效索引
toupper(word[0]) - 'A'
存在返回 [0...N)
之外的值的风险。 更好的是
(toupper(word[0]) - 'A')%((unsigned)N)
LENGTH
未知,可能太小
for(node *ptr = tmp; ptr != NULL; ptr = ptr->next)
在
load()
中,for
循环不清楚。 这里尝试了什么? if (ptr->next == NULL)
看起来总是正确的。 杂散代码?
次要:类型不一致
为什么
int counter
,但函数返回一个unsigned
?
unsigned int size(void) {
int counter = 0;
...
return counter;
}