CS50 Pset5 无法释放 malloc 导致的内存泄漏

CS50 Pset5 trouble freeing memory leak caused by malloc

提问人:jake 提问时间:4/1/2023 更新时间:4/1/2023 访问量:81

问:

我是stack-overflow的新手,所以对我来说是裸露的

我正在服用 CS50 和 im 在 Pset5(拼写器)上我的代码按预期工作,但是当我运行 check50 函数时,我的程序使 Valgrind 失败,Valgrind 在不添加 -s 的情况下找不到泄漏。 我确信这与释放第 94 行的 malloc 有关,但仍然不确定我哪里出错了。

如果您能帮助我解决这个问题,我将不胜感激。

链接到 CS50 Pset5:https://cs50.harvard.edu/x/2023/psets/5/speller/

请参阅下面的代码:

// Implements a dictionary's functionality

#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include "dictionary.h"

// Represents a node in a hash table
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
}
node;

// 26 buckets as there are only 26 letters in the alphabet(regardless of starting from 1 there will still only be 26 buckets needed)
const unsigned int N = 1e6 + 9;

// Hash table
node *table[N];

//Unsigned int will encode only nonnegative integers to my delcared variables
unsigned int hash_value;
unsigned int word_count;

// Returns true if word is in dictionary, else false
bool check(const char *word)
{
    // take the inputted hash and output a numerical value that refers to the bucket value
    hash_value = hash(word);
    node *cursor = table[hash_value];

    while(cursor != 0)
    {
        if (strcasecmp(word, cursor-> word) == 0)
        {
            return true;
        }
        else
        {
            cursor = cursor ->next;
        }
    }
    return false;
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    //information pulled on hash function from:
    //https://cp-algorithms.com/string/string-hashing.html
    //map char array into an integer and compare those instead of the strings
    const int p = 29;
    const int m = 1e6 + 9;
    //initialise variable
    long long hash = 0;
    //start from 1 as a starting from 0 could result in a collision after calculation(if a is 0 aaa will also be 0)
    long long p_pow = 1;
    //loops through the char array
    for (int i = 0; i < strlen(word); i++)
    {
        //iterates each char as a upper case
        char c = toupper(word[i]);
        //calculates the hash_value by subtracting the ascii value and adding back the 1 from p_pow
        hash = (hash + (c - 'A' + 1) * p_pow) % m;
        //modulo keeps integer overflow to a minimum
        p_pow = (p_pow * p) % m;
    }
    return hash;
}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
    //opens file dictionary to read the contents
    FILE *file = fopen(dictionary, "r");
    //returns null if file couldn't be opened
    if (file == NULL)
    {
        return false;
    }

    //declare variable called word;
    char word[LENGTH + 1];

    //While only reading strings reading through the file with fscan allocate memory for a new node
    while(fscanf(file, "%s", word) == 1)
    {
        //allocate the memory for a node and store the address of that node inside of n
        node *n = malloc(sizeof(node));
        //return False if memory allocation fails, do this by checking if n is equal to NULL(this tells if this is the first in the list)
        if (n == NULL)
        {
        return false;
        }
        else
        {
         //pointer to the destination array where the string is to be copied
         strcpy(n->word, word);
         //increment word count based on number of words being pulled from the dictionary
         word_count++;
         //after hashing the word store the number value in a variable
         hash_value = hash(word);
         //set the pointer of the new node to the front of the table
         n->next = table[hash_value];

         //
         table[hash_value] = n;
        }

    }
    //free up system resources that are using the file once finished
    fclose(file);
    return true;

}

// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
    // return the size of dictionary
    if(word_count > 0)
    {
        return word_count;
    }
    return 0;
}

// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
    // to free memory used by malloc
    for(int i = 0; i < N; i++)
    {
        node *cursor = table[i];
        while (cursor != NULL)
        {
            node *tmp = cursor;
            cursor = cursor->next;
            free(tmp);
            if (cursor == NULL)
            {
            return true;
            }
        }
    }
    return false;
}

//////////
//Load  which responsible for loading the data into a hash table
//Hash  take a word run a hash function returning a number that corrisponds to that word
//Size  which returns how many words are in the dictionary
//Check  which answers whether the word is in the dictionary or not
//Unload  free the memory that you allocated

运行下面的 Check50 结果

:(程序没有内存错误

Valgrind 测试失败;有关详细信息,请参阅日志。


running valgrind --show-leak-kinds=all --xml=yes --xml-file=/tmp/tmpgldh6b_g -- ./speller substring/dict substring/text...
checking for output "MISSPELLED WORDS\n\nca\ncats\ncaterpill\ncaterpillars\n\nWORDS MISSPELLED: 4\nWORDS IN DICTIONARY: 2\nWORDS IN TEXT: 6\n"...
checking that program exited with status 0...
checking for valgrind errors...
56 bytes in 1 blocks are still reachable in loss record 1 of 1: (file: dictionary.c, line: 94)

如果您想了解更多信息,请随时询问。

我尝试在多个其他论坛上搜索释放 malloc 的方法,并希望确保我已关闭所有打开的文件。

C 内存泄漏 Malloc Valgrind CS50

评论

0赞 Some programmer dude 4/1/2023
编辑您的问题,向我们展示一个适当的最小可重复示例。您如何使用当前显示的功能?您的简单测试功能在哪里?main
1赞 Retired Ninja 4/1/2023
循环在释放第一个节点链后返回。您应该删除该代码。在那里返回 true 或 false 也没有多大意义。该函数中没有成功或失败的情况。unload
0赞 Fe2O3 4/2/2023
OT 提示:当添加单词时(来自假定已排序的字典),任何中给定哈希值的第一个单词将是列表中的“最大值”单词......例如,在沿着该链表搜索“dogg”时,当遇到“cat”(或缺少“dogg”之前的任何单词时,可以停止搜索(未找到)。无需继续与“蝙蝠”和“土豚”进行比较......

答:

1赞 Zwy 4/1/2023 #1

unload() 的实现应该是

void unload(void) {
    for(int i = 0; i < N; i++) {
        node *cursor = table[i];
        while (cursor != NULL) {
            node *tmp = cursor;
            cursor = cursor->next;
            free(tmp);
        }
    }
 }
  1. 根据您的逻辑,该函数应该返回 nothing 而不是 bool。
  2. while 循环中的返回值在第一次迭代中终止 for 循环。然后 table 的 elements[1...n - 1] 没有被释放,导致内存泄漏。

评论

0赞 jake 4/3/2023
感谢您的全面回复,这是我最初的想法,但由于 CS50 给出的分发代码,卸载必须保留一个布尔函数,但是您确实通过通知我我的 for 循环:)不足来帮助我找到了解决方案