我现在正在研究哈佛大学的cs50的pset6,问题是要实现一个trie词典。 我终于设法使它工作一个小问题。无法正确释放内存
- 当我运行valgrind检查内存泄漏时,它告诉我我释放了比分配的多一个,但在卸载函数中看不到任何问题。
- 它也警告我有一些未初始化的值,但我无法弄清楚,尽管它不会影响结果。
这里是我的全部代码:
/****************************************************************************
* dictionary.c
*
* Computer Science 50
* Problem Set 6
*
* valgrind warn that there are uninitialized values, could be the node struct, but don't
* know how to initialize it, anyway, it works at last!
*
* Implements a dictionary's functionality.
***************************************************************************/
#include <stdbool.h>
#include <ctype.h>
#include "dictionary.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define HASHTABLE_SIZE 5000
int count = 0; // gloabal counter
typedef struct node { // data structure
bool end_word;
struct node *children[27];
} node;
int
charNumber(char c); // function prototype
void
freeNode(node *currentNode);
node root = {false,{NULL}};
/*
* Returns true if word is in dictionary else false.
*/
bool
check(const char *word)
{
node *ptr = &root;
for (int i=0;i<strlen(word);i++)
{
if (ptr->children[charNumber(word[i])] == NULL)
return false;
ptr = ptr->children[charNumber(word[i])];
}
if (ptr->end_word)
return true;
else
return false;
}
/*
* Loads dictionary into memory. Returns true if successful else false.
*/
bool
load(const char *dictionary)
{
// char word[LENGTH+1]; // must initialize to zero! Or there will be some weird problem.
FILE *fp = fopen(dictionary,"r");
if (fp == NULL)
return false;
while (!feof(fp))
{
char word[LENGTH+1] = {};
fscanf(fp,"%s\n",word); // have to use "%s\n" instead of "%s", or the count will be wrong, don't know why.
count++;
node *ptr = &root;
for (int i=0;i<strlen(word);i++)
{
if (ptr->children[charNumber(word[i])] == NULL)
{
node *new = malloc(sizeof(node));
*new = (node) {false,{NULL}}; // initiallization
ptr->children[charNumber(word[i])] = new;
ptr = new;
}
else
{
ptr = ptr->children[charNumber(word[i])];
}
}
ptr->end_word = true;
}
fclose(fp);
return true;
}
/*
* caculate a number for the character
*/
int
charNumber(char c)
{
int num;
if (c == '\'')
return 26;
else if(c >= 'A' && c <= 'Z')
c += 32;
num = c - 'a';
return num;
}
/*
* Returns number of words in dictionary if loaded else 0 if not yet loaded.
*/
unsigned int
size(void)
{
if (count)
return count;
else
return 0;
}
/*
* Unloads dictionary from memory. Returns true if successful else false.
*/
bool
unload(void)
{
freeNode(&root);
return true; // can't figure out when to return false...
}
void freeNode(node *currentNode)
{
for (int i=0;i<27;i++)
{
if (currentNode->children[i] != NULL)
freeNode(currentNode->children[i]);
}
free(currentNode);
}
下面是一些Valgrind的输出:
==22110== Invalid free()/delete/delete[]
==22110== at 0x4024ECD: free (vg_replace_malloc.c:366)
==22110== by 0x8048F90: freeNode (dictionary_tries.c:152)
==22110== by 0x8048F45: unload (dictionary_tries.c:141)
==22110== by 0x8048AB5: main (speller.c:158)
==22110== Address 0x804a5a0 is 0 bytes inside data symbol "root"
==22110==
--22110-- REDIR: 0x40b2930 (strchrnul) redirected to 0x4028570 (strchrnul)
==22110==
==22110== HEAP SUMMARY:
==22110== in use at exit: 0 bytes in 0 blocks
==22110== total heap usage: 367,083 allocs, 367,084 frees, 41,113,776 bytes allocated
==22110==
==22110== All heap blocks were freed -- no leaks are possible
==22110==
==22110== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 14 from 9)
==22110==
==22110== 1 errors in context 1 of 1:
==22110== Invalid free()/delete/delete[]
==22110== at 0x4024ECD: free (vg_replace_malloc.c:366)
==22110== by 0x8048F90: freeNode (dictionary_tries.c:152)
==22110== by 0x8048F45: unload (dictionary_tries.c:141)
==22110== by 0x8048AB5: main (speller.c:158)
==22110== Address 0x804a5a0 is 0 bytes inside data symbol "root"
==22110==
--22110--
--22110-- used_suppression: 14 U1004-ARM-_dl_relocate_object
==22110==
==22110== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 14 from 9)
不使用'new'变量名。 – taocp
请不要用C++标记C代码;这些语言本身的内存管理技术完全不同。在C++中,'new'是一个关键字,所以你的代码是严格的,但严格来说是C代码,与C++无关。 –
如果'valgrind'给你警告,那么你应该在问题中包含那些警告的代表性例子。如果'valgrind'没有给出精确的行号,则需要在启用调试的情况下编译代码(通常为'-g'),以便它可以告诉您哪些行号出错。请注意,'malloc()'不会初始化内存。当你第一次分配它时,你似乎没有初始化结构中的“子”部分,这会导致问题。 –