如何更改我的代码(哈希表+链表)以避免内存泄漏?
How to change my code (hashtable + linked lists) to avoid memory leaks?
抱歉,如果以下问题看起来很幼稚,我是编程的新手,仍然在基本概念上挣扎。
我编写了一段代码,将字典(txt 文件)加载到 hashtable/linked 列表数据结构中,程序编译并提供预期结果,但 Valgrind 显示内存泄漏。所有主要功能(节点的创建、人口、搜索、打印)工作正常,除了 destroy 功能(一个用于从 mallocs 释放内存)。 Valgrind 显示内存泄漏。
我在这里错过了什么?如何使我的代码更简洁?
感谢您的宝贵时间!你真棒!
我知道至少部分原因是我填充数据结构的方法 - 我使用 2 个 char* 缓冲区(tmp 和 tmp1,在循环内部和外部)来处理来自 fscanf 的输入到节点中。我想在卸载期间我可以在外部缓冲区 ("tmp") 上使用 'free' 函数,但不能在内部缓冲区 ("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 != '[=12=]'; 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);
}
}
// 主要功能 - 将 txt 文件中的字符串放入 hashtable/linked 列表数据结构
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 == '[=19=]')
{
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 能够证明在我的程序运行期间没有内存泄漏。
问题包括
您对散列 table 的原始初始化有问题:
for (int i = 0; i < hash_elements; i++)
{
hashtable[i] = malloc(sizeof(node));
if (hashtable[i] == NULL)
{
return 1;
}
}
您正在为节点分配 space,但您没有使用有效数据填充节点。这种分配似乎完全没有用。看起来将 NULL
分配给 hashtable 元素不仅更容易而且更正确:
for (int i = 0; i < hash_elements; i++) {
hashtable[i] = NULL;
}
根据我对您的其他代码的了解,我认为它甚至可以在不进行其他更改的情况下处理该问题。
不清楚为什么需要为 tmp
动态分配 space。您似乎没有对它做任何需要动态分配的事情——没有运行时确定的长度,没有超出其声明范围的使用,……——所以将它声明为普通的可能会更清晰、更容易数组:
char tmp[41];
您可以按照显示的所有相同方式使用该版本的 tmp
,并且无需释放它。
您为输入文本的副本动态分配 space,但从未释放它。指向那些 space 的指针最初记录在 tmp1
中,然后分配给您节点的 word
成员。您不需要或不想释放 tmp1
中保存的任何值,因此,因为您仍在使用这些指针,但是您的 destroy()
函数可以而且应该在释放每个节点的 word
之前释放它节点.
抱歉,如果以下问题看起来很幼稚,我是编程的新手,仍然在基本概念上挣扎。
我编写了一段代码,将字典(txt 文件)加载到 hashtable/linked 列表数据结构中,程序编译并提供预期结果,但 Valgrind 显示内存泄漏。所有主要功能(节点的创建、人口、搜索、打印)工作正常,除了 destroy 功能(一个用于从 mallocs 释放内存)。 Valgrind 显示内存泄漏。
我在这里错过了什么?如何使我的代码更简洁?
感谢您的宝贵时间!你真棒!
我知道至少部分原因是我填充数据结构的方法 - 我使用 2 个 char* 缓冲区(tmp 和 tmp1,在循环内部和外部)来处理来自 fscanf 的输入到节点中。我想在卸载期间我可以在外部缓冲区 ("tmp") 上使用 'free' 函数,但不能在内部缓冲区 ("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 != '[=12=]'; 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);
}
}
// 主要功能 - 将 txt 文件中的字符串放入 hashtable/linked 列表数据结构
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 == '[=19=]')
{
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 能够证明在我的程序运行期间没有内存泄漏。
问题包括
您对散列 table 的原始初始化有问题:
for (int i = 0; i < hash_elements; i++) { hashtable[i] = malloc(sizeof(node)); if (hashtable[i] == NULL) { return 1; } }
您正在为节点分配 space,但您没有使用有效数据填充节点。这种分配似乎完全没有用。看起来将
NULL
分配给 hashtable 元素不仅更容易而且更正确:for (int i = 0; i < hash_elements; i++) { hashtable[i] = NULL; }
根据我对您的其他代码的了解,我认为它甚至可以在不进行其他更改的情况下处理该问题。
不清楚为什么需要为
tmp
动态分配 space。您似乎没有对它做任何需要动态分配的事情——没有运行时确定的长度,没有超出其声明范围的使用,……——所以将它声明为普通的可能会更清晰、更容易数组:char tmp[41];
您可以按照显示的所有相同方式使用该版本的
tmp
,并且无需释放它。您为输入文本的副本动态分配 space,但从未释放它。指向那些 space 的指针最初记录在
tmp1
中,然后分配给您节点的word
成员。您不需要或不想释放tmp1
中保存的任何值,因此,因为您仍在使用这些指针,但是您的destroy()
函数可以而且应该在释放每个节点的word
之前释放它节点.