简体   繁体   English

如何更改我的代码(哈希表+链表)以避免内存泄漏?

[英]How to change my code (hashtable + linked lists) to avoid memory leaks?

Sorry if the following question seems naive, I'm very new to programming and still struggle with basic concepts. 很抱歉,如果以下问题看起来很幼稚,我对编程还是很陌生,但仍然对基本概念感到困惑。

I have written a code that loads a dictionary (txt file) into a hashtable/linked list data structure, the program compiles and provides expected results, but Valgrind shows memory leaks. 我编写了将字典(txt文件)加载到哈希表/链接列表数据结构中的代码,该程序编译并提供了预期的结果,但是Valgrind显示内存泄漏。 All major functions (creation of nodes, population, search, print) work fine, except the destroy function (one used to free memory from the mallocs). 所有的主要功能(创建节点,填充,搜索,打印)都可以正常工作,但销毁功能(用于从malloc释放内存的功能)除外。 Valgrind shows memory leaks. Valgrind显示内存泄漏。

What am I missing here? 我在这里想念什么? How to make my code cleaner? 如何使我的代码更整洁?

Thanks for your time! 谢谢你的时间! You're awesome! 你真棒!

I know that at least partly the reason is my approach to populating the data structure - I use 2 char* buffers (tmp and tmp1, inside and outside of the loop) to juggle inputs from fscanf into the nodes. 我至少知道部分原因是我填充数据结构的方法-我使用2个char *缓冲区(tmp和tmp1,在循环的内部和外部)将输入从fscanf杂凑到节点中。 I guess during unloading I can use the 'free' function on the outside buffer ("tmp"), but not on the inside buffer ("tmp1"). 我想在卸载期间,我可以在外部缓冲区(“ tmp”)上使用“ free”功能,但不能在内部缓冲区(“ tmp1”)上使用。

I tried using 1 buffer (both inside and outside the loops) and 2 buffers initialised statically (char tmp[]). 我尝试使用1个缓冲区(循环内外)和2个静态初始化的缓冲区(char tmp [])。 Both methods populate the structure with just 1 word (last in the dictionary). 两种方法都只用1个单词填充结构(在字典中排在最后)。

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

// define node structure //定义节点结构

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

// hash size variable and hash function (thanks, K&R) //散列大小变量和散列函数(感谢,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;
}

// function to add node to beginning of ll //将节点添加到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;
}

// function to free allocated memory //函数释放已分配的内存

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

// MAIN FUNCTION - puts word strings from txt files into hashtable/linked list data structures //主功能-将txt文件中的单词字符串放入哈希表/链接列表数据结构中

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

// make an array for linked lists (hashtable) and allocate memory for heads //为链接列表(哈希表)创建一个数组,并为heads分配内存

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

// open dictionary file for reading //打开字典文件以供阅读

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

// define temporary storage for strings //定义字符串的临时存储

char* tmp = malloc(41);

// scan file for strings and populate linked lists //扫描文件以查找字符串并填充链接列表

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;
                }
            }
        }
    }
}

// unload dictionary //卸载字典

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

return 0;

} }

I expect Valgrind to show that there were no memory leaks during the work of my program. 我希望Valgrind能够表明在我的程序工作期间没有内存泄漏。

Issues include 问题包括

  1. Your original initialization of the hash table is questionable: 您对哈希表的原始初始化值得怀疑:

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

    You are allocating space for nodes, but you do not populate the nodes with valid data. 您正在为节点分配空间,但是没有使用有效数据填充节点。 This allocation appears to be utterly useless. 这种分配似乎完全没有用。 It looks like it would be not only easier but also more correct to assign NULL to the hashtable elements: 为散列表元素分配NULL看起来不仅更容易,而且更正确:

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

    From what I can see of your other code, I think it will even handle that without other changes. 从您看到的其他代码来看,我认为它甚至可以在不进行其他更改的情况下进行处理。

  2. It's unclear why you need to allocate space dynamically for tmp . 目前尚不清楚为什么需要为tmp动态分配空间。 You do not seem to do anything with it that would require dynamic allocation -- no runtime-determined length, no use outside the scope of its declaration, ... -- so it would probably be cleaner and easier to declare it as an ordinary array: 您似乎不需要执行任何需要动态分配的操作-没有运行时确定的长度,没有在声明的范围之外使用...-因此,将其声明为普通变量可能更干净,更容易数组:

     char tmp[41]; 

    You can use that version of tmp in all the same ways you show it being used now, and you don't need to free it. 您可以按照显示当前正在使用的所有相同方式来使用该版本的tmp ,而无需释放它。

  3. You dynamically allocate space for a copy of the input text, but never free it. 您为输入文本的副本动态分配空间,但是从不释放它。 The pointers to those spaces are initially recorded in tmp1 , and then assigned to the word members of your nodes. 指向这些空格的指针最初记录在tmp1 ,然后分配给节点的word成员。 You do not need or want to free any value held in tmp1 , therefore, because you are still using those pointers, but your destroy() function can and should free each node's word before freeing that node. 因此,您不需要或不想释放tmp1保存的任何值,因为您仍在使用这些指针,但是destroy()函数可以并且应该在释放该节点之前释放每个节点的word

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM