我收到分段错误:加载字符串列表时核心转储,特别是对于长列表。有一个我无法摆脱的内存泄漏

问题描述 投票:0回答:1

我正在编写一个代码,该代码应该加载字典并检查文本中的单词是否正确。

代码在使用小列表(用于字典)时进行编译,但当我使用大列表运行它时,我收到“分段错误:核心转储”。

我用 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)`
c memory-leaks linked-list hashtable valgrind
1个回答
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;
}
© www.soinside.com 2019 - 2024. All rights reserved.