内存泄漏:块仍然可以访问 - 如何解决?

Memory leak: blocks are still reachable - How to solve it?

我在这里找到了一些类似的主题: Valgrind - blocks still reachable due to? 或在这里: Still Reachable Leak detected by Valgrind 但是 none 提供了解决方案。因此,我想再次提出这个问题。

这是 valgrind 的结果:

==403== HEAP SUMMARY:
==403==     in use at exit: 7,791,056 bytes in 139,126 blocks
==403==   total heap usage: 143,096 allocs, 3,970 frees, 8,023,256 bytes allocated
==403== 
==403== 7,791,056 bytes in 139,126 blocks are still reachable in loss record 1 of 1
==403==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==403==    by 0x401BBE: load (dictionary.c:121)

这是我为新节点分配内存的代码:

#include <stdbool.h>
#include <stdio.h>
#include "dictionary.h"
#include <ctype.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>

// Represents a node in a hash table
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
}
node;

// ADDITIONAL functions
bool free_list(node *w);
unsigned int numeric(char c);

// Number of buckets in hash table
const unsigned int N = 26 * 28 * 28;
// Initialize number of word count in dictionary
int word_count = 0;

// Hash table
node *table[N];

// Returns true if word is in dictionary, else false
bool check(const char *word)
{
    // TODO: initiate a node before looping
    node *n = table[hash(word)];
    // Checks if malloc succeeded, returns false if not
    while (true)
    {
        if (n == NULL)
        {
            return false;
        }
        else if (strcasecmp(n->word, word) == 0)
        {
            return true;
        }
        else
        {
            n = n->next;
        }
    }
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    // TODO
    int ret;
    ret = tolower(word[0]) - 97 + 26 * numeric(word[1]);
    if (word[1] != '[=11=]')
    {
        ret += 26 * 28 * numeric(word[2]);
    }
    return ret;
}

// ADDITION: Numeric char value
unsigned int numeric(char c)
{
    int ret;
    if (isalpha(c))
    {
        ret = (tolower(c) - 96);
    }
    else if (c == '[=11=]')
    {
        ret = 0;
    }
    else
    {
        ret = 27;
    }
    return ret;
}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
    // TODO: Open file
    FILE *dict = fopen(dictionary, "r");
    if (dict == NULL)
    {
        printf("Could not open dictionary.\n");
        return false;
    }

    //Initialize the list to be NULL
    for (int i = 0; i < N; i += 1)
    {
        table[i] = NULL;
    }

    // COPY words in dictionary to hash table
    char c;
    char tmp[LENGTH + 1];
    int index = 0;
    while (fread(&c, sizeof(char), 1, dict))
    {
        if (c != '\n')
        {
            tmp[index] = c;
            index += 1;
        }
        else // means this is a full word
        {
            // Terminate current word
            tmp[index] = '[=11=]';

            // Put the word to the hash
            // allocate memory to a new node
            node *n = malloc(sizeof(node));
            // Checks if malloc succeeded, returns false if not
            if (n == NULL)
            {
                unload();
                return false;
            }
            // assign values to new node
            n->next = table[hash(tmp)];
            strcpy(n->word, tmp);

            // make the hash point to the new node
            table[hash(tmp)] = n;

            // Clear the tmp array for next word
            for (int i = 0; i < index + 1; i += 1)
            {
                tmp[index] = 0;
            }
            word_count += 1; //Increase word count for size function
            index = 0;
        }
    }
    fclose(dict);
    return true;
}

// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
    // TODO
    return word_count;
}

// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
    // TODO
    for (int i = 0; i < N; i += 1)
    {
        free_list(table[i]);
    }

    return true;
}

// Create free linked-list function
bool free_list(node *w)
{
    if (w == NULL)
    {
        return true;
    }
    else if (w->next == NULL)
    {
        free(w);

        return true;
    }
    else
    {
        free_list(w->next);
        return true;
    }

}

HEAP SUMMARY报告问题的第121行是新节点的malloc行,在load()函数中:node *n = malloc(sizeof(node));

你能告诉我为什么会导致内存泄漏以及我该如何解决这个问题吗?

你的 free_list 函数有一个问题。

这个递归函数只会释放列表中的最后一个节点。这可以重写为不是递归的,但你可能想要在最后一个“else”代码块中是

free_list(w->next);
free(w);
return true;

顺便说一句,为什么这个函数有一个 return 值?它始终 return 为真,并且您永远不会在调用函数时检查 return 值。

你的 free 函数只释放最后一个元素的内存:

bool free_list(node *w)
{
    if (w == NULL) // empty : no free
    {
        return true;
    }
    else if (w->next == NULL) // free last element 
    {
        free(w);
        return true;
    }
    else
    {
        free_list(w->next); // try to free next element but not current
        return true;
    }
}

应修改为:

bool free_list(node *w)
{
    if (w == NULL)
    {
        return true;
    }
    // free next elements
    if (w->next != NULL)
    {
        free_list(w->next);
    }
    // free current
    free(w);
    return true;
}