【问题标题】:can't free memory correctly无法正确释放内存
【发布时间】:2023-03-11 21:25:01
【问题描述】:

我现在正在研究哈佛 cs50 的 pset6,问题是要实现一个 trie 字典。 我终于设法解决了一个小问题。

  1. 当我运行 valgrind 检查内存泄漏时,它告诉我释放的内存比分配的多,但我在卸载函数中看不到任何问题。
  2. 它还警告我有一些未初始化的值,但我无法弄清楚,尽管它不会影响结果。

这是我的全部代码:

/****************************************************************************
 * 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作为变量名。
  • 请不要用 C++ 标记 C 代码;语言原生的内存管理技术是完全不同的。而在 C++ 中,new 是一个关键字,所以你的代码是严格但严格的 C 代码,与 C++ 无关。
  • 如果valgrind 向您发出警告,那么您应该在问题中包含这些警告的代表性示例。如果valgrind 没有给你精确的行号,你需要在启用调试的情况下编译代码(通常是-g),以便它可以告诉你哪些行号出错了。注意malloc()不会初始化内存;首次分配结构的children 部分时,您似乎没有对其进行初始化,这会导致问题。
  • 事实上,C 标记 wiki 非常清楚您不应该将 C 和 C++ 标记在一起。除此之外,我希望你不要试图freeNode(&amp;root);...
  • 您正在尝试释放您未分配的root

标签: c cs50


【解决方案1】:

假设您的load 函数打开一个空白文件。 feof(fp) 最初将返回0,因为尚未使用读取操作; EOF 标志只会在读取操作返回指示错误的值后设置。这就是错误所在。在您的情况下,您需要循环 fscanf(fp,"%s\n",word); 的返回值而不是 feof 的返回值。例如:

while (fscanf(fp, "%s", word) == 1) {
    /* ... */
}

if (feof(fp)) {
    /* The loop ended due to EOF */
}
else if (ferror(fp)) {
    /* The loop ended due to some file input error */
}
else {
    /* The loop ended because the input was invalid
     * (this applies to input where a conversion is
     *  required eg. the conversion in %d, %u, %f, etc... */
}

详细地说,feof 仅用于确定上次读取失败的原因

在空白文件的情况下会导致此类警告的原因是word 将包含不确定的信息。

此外,freeNode(&amp;root); 是错误的,因为 free 只能在由 callocreallocmalloc 返回的指针上调用。

【讨论】:

    【解决方案2】:
    node root = {false,{NULL}};
    

    没有在堆上分配,但是你尝试像它一样释放它

    unload(void)
    {
        freeNode(&root);
    

    【讨论】:

    • 其实root是一个全局变量,因此不太可能分配到栈上。没错,它不是动态分配的,因此不应该被释放。
    • 实现决定将它放在哪里。记住,栈和堆在同一个地方...
    • @xaxxon - 第 42 行(有多少问题比数字答案好笑!)
    • @enhzflep 不确定你是认真的还是想搞笑,但我的意思是分配的变量的内存在哪里。
    • @xaxxon 是的,这正是我出错的地方,但如何解决呢?我也尝试为 root 分配内存,但没有运气,您似乎只能在 c99 中全局初始化编译时常量表达式。
    猜你喜欢
    • 2012-02-28
    • 1970-01-01
    • 2020-06-23
    • 2012-01-22
    • 1970-01-01
    • 2020-11-28
    • 2013-07-30
    相关资源
    最近更新 更多