【问题标题】:How to change my code (hashtable + linked lists) to avoid memory leaks?如何更改我的代码(哈希表 + 链表)以避免内存泄漏?
【发布时间】:2019-05-21 06:47:06
【问题描述】:

很抱歉,如果以下问题看起来很幼稚,我对编程很陌生,但仍然在基本概念上挣扎。

我编写了一个代码,将字典(txt 文件)加载到哈希表/链表数据结构中,程序编译并提供预期结果,但 Valgrind 显示内存泄漏。所有主要功能(创建节点、填充、搜索、打印)都可以正常工作,除了破坏功能(用于从 malloc 中释放内存的功能)。 Valgrind 显示内存泄漏。

我在这里缺少什么?如何让我的代码更干净?

感谢您的宝贵时间!你太棒了!

我知道,至少部分原因是我填充数据结构的方法 - 我使用 2 个 char* 缓冲区(tmp 和 tmp1,循环内部和外部)来处理来自 fscanf 的输入到节点中。我想在卸载期间我可以在外部缓冲区(“tmp”)上使用“免费”功能,但不能在内部缓冲区(“tmp1”)上使用。

我尝试使用 1 个缓冲区(循环内部和外部)和 2 个静态初始化的缓冲区(char tmp[])。两种方法都只用 1 个单词(字典中的最后一个单词)填充结构。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//定义节点结构

typedef struct node
{
    char* word;
    struct node* next;
}node;

//散列大小变量和散列函数(感谢K&R)

const int hash_elements = 100;

unsigned hash(char *s)
{
    unsigned hashval;
    for (hashval = 0; *s != '\0'; s++)
    hashval = *s + 31 * hashval;
    return hashval % hash_elements;
}

// 将节点添加到 ll 开头的函数

int addnode_start (node** head, char* word)
{
    node* new_node = malloc(sizeof(node));
    if (new_node == NULL)
    {
        return 1;
    }
    new_node->word = word;
    new_node->next = *head;
    *head = new_node;
    return 0;
}

//释放分配内存的函数

void destroy (node* hashtable)
{
    if (hashtable == NULL)
    {
        return;
    }
    else
    {
        destroy(hashtable->next);
        free(hashtable);
    }
}

// MAIN FUNCTION - 将 txt 文件中的单词字符串放入哈希表/链表数据结构中

int main (int argc, char* argv[])
{
    if (argc < 2)
    {
        printf("Usage: ./stringdll dictionary.txt\n");
        return 1;
    }

// 为链表(哈希表)创建一个数组并为头分配内存

node* hashtable[hash_elements];
for (int i = 0; i < hash_elements; i++)
{
    hashtable[i] = malloc(sizeof(node));
    if (hashtable[i] == NULL)
    {
        return 1;
    }
}

//打开字典文件进行阅读

char* infile = argv[1];
FILE* input = fopen(infile, "r");
if (input == NULL)
{
    printf("File does not exist.\n");
    return 1;
}

// 为字符串定义临时存储

char* tmp = malloc(41);

// 扫描文件中的字符串并填充链表

while(fscanf(input, "%s", tmp) != EOF)
{
    char* tmp1 = malloc(41);
    for (int h = 0; h < hash_elements; h++)
    {
        if (hash(tmp) == h)
        {
            if (hashtable[h]->word == '\0')
            {
                hashtable[h]->word = strcpy(tmp1, tmp);
                hashtable[h]->next = NULL;
            }
            else
            {
                int tmp_0 = addnode_start(&hashtable[h], strcpy(tmp1, tmp));
                if (tmp_0 == 1)
                {
                    return 1;
                }
            }
        }
    }
}

// 卸载字典

for (int d = 0; d < hash_elements; d++)
{
    destroy (hashtable[d]);
}

return 0;

}

我希望 Valgrind 显示在我的程序工作期间没有内存泄漏。

【问题讨论】:

  • 你没有fclose你的文件。你永远不会freetmptmp1
  • 我不明白这个问题,valgrind 会准确地告诉你你没有发布什么——释放它们。
  • 您没有释放存储在哈希表中的单词。
  • 你的数组是一个节点的数组。它可以是指向节点的 指针 数组,指向链的头部。 (并初始化为 NULL)

标签: c linked-list hashtable


【解决方案1】:

问题包括

  1. 你原来的哈希表初始化有问题:

    for (int i = 0; i < hash_elements; i++)
    {
        hashtable[i] = malloc(sizeof(node));
        if (hashtable[i] == NULL)
        {
            return 1;
        }
    }
    

    您正在为节点分配空间,但您没有使用有效数据填充节点。这种分配似乎完全没有用。看起来将NULL 分配给哈希表元素不仅更容易而且更正确:

    for (int i = 0; i < hash_elements; i++) {
        hashtable[i] = NULL;
    }
    

    根据我对您其他代码的了解,我认为它甚至可以在不进行其他更改的情况下处理。

  2. 不清楚为什么需要为tmp 动态分配空间。您似乎没有对它做任何需要动态分配的事情——没有运行时确定的长度,没有超出其声明范围的用途,......——所以将它声明为普通的可能会更干净、更容易数组:

    char tmp[41];
    

    您可以使用该版本的tmp,就像您现在使用它时一样,您不需要释放它。

  3. 您为输入文本的副本动态分配空间,但从不释放它。指向这些空间的指针最初记录在tmp1 中,然后分配给节点的word 成员。因此,您不需要或不想释放 tmp1 中保存的任何值,因为您仍在使用这些指针,但您的 destroy() 函数可以而且应该在释放每个节点之前释放每个节点的 word

【讨论】:

  • 嗨,约翰。感谢您的输入。我已经为哈希表的初始化分配了 NULL 指针,并相应地更改了循环参数(从检查内容 == \0 到检查是否 hashtable[h] == NULL。这行得通,所以谢谢。
  • 但是,如果我将 tmp 和 tmp 都初始化为 char tmp[41] 和 char tmp[41] 我的打印函数会将所有节点内容打印为具有相同的值(字典中的最后一个单词)。我实际上提到我尝试了静态初始化,但没有奏效。
  • @MaximBiryukov,我没有说将 both tmptmp1 声明为普通数组。在这方面,我的 cmets 专门针对tmp。此外,我的第三点特别假设 tmp1 仍被声明为指针,您可以为其动态分配空间(然后将哪个指针值分配给节点的 word 元素)。
  • JohnBollinger,对不起,我是新手,所以这些东西似乎势不可挡。外部缓冲区的静态初始化工作。同样正如@Osiris 提到的,我最后关闭了文件。 Valgrind 仍然显示“10 个块中的 160 个字节肯定丢失了”...
  • 供参考:我的字典只有 10 个字。 Valgrind 说我的程序做了 21 次分配和 11 次释放。
猜你喜欢
  • 2018-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多