简体   繁体   English

如何理解 valgrind 输出的内存泄漏?

[英]How can I understand a memory leak from valgrind output?

I'm taking the CS50 class and I'm on Week 5 and trying to figure out Pset5 Speller.我正在参加 CS50 课程,我在第 5 周并试图找出 Pset5 拼写器。 For anyone who isn't familiar, the goal is to edit a particular .c file to get five functions to run properly so that the main function (located in a separate file) can do the following:对于不熟悉的人,目标是编辑特定的 .c 文件以使五个函数正常运行,以便主函数(位于单独的文件中)可以执行以下操作:

  1. LOAD - load a dictionary into a hash table LOAD - 将字典加载到哈希表中
  2. HASH - run a word through this function to help load it into the dictionary or search for the word later HASH - 通过此功能运行一个单词以帮助将其加载到字典中或稍后搜索该单词
  3. SIZE - see how many words are in the dictionary SIZE - 查看字典中有多少单词
  4. CHECK - to see if a word from a text is in the dictionary检查 - 查看文本中的单词是否在字典中
  5. UNLOAD - release the dictionary so there are no memory leaks UNLOAD - 释放字典所以没有内存泄漏

Please note that the file was given to me in the class and I am to edit the space within the functions - the only other thing I can change is const unsigned int N = 1000;请注意,该文件是在课堂上提供给我的,我要编辑函数中的空间 - 我唯一可以更改的另一件事是const unsigned int N = 1000; which I set to 1000 just as an arbitrary number, but it could be anything.我将其设置为 1000 作为一个任意数字,但它可以是任何数字。

I am having trouble with only one thing (that I can tell).我只有一件事(我可以说)有问题。 I have done everything to make it run, but Check50 (the program to tell if I did it correctly) is telling me that I have memory errors:我已经做了一切让它运行,但 Check50(判断我是否正确执行的程序)告诉我我有内存错误:

Results for cs50/problems/2021/x/speller generated by check50 v3.3.0
:) dictionary.c exists
:) speller compiles
:) handles most basic words properly
:) handles min length (1-char) words
:) handles max length (45-char) words
:) handles words with apostrophes properly
:) spell-checking is case-insensitive
:) handles substrings properly
:( program is free of memory errors
    valgrind tests failed; see log for more information.

When I run valgrind this is what it gives me:当我运行 valgrind 这就是它给我的:

==347== 
==347== HEAP SUMMARY:
==347==     in use at exit: 472 bytes in 1 blocks
==347==   total heap usage: 143,096 allocs, 143,095 frees, 8,023,256 bytes allocated
==347== 
==347== 472 bytes in 1 blocks are still reachable in loss record 1 of 1
==347==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==347==    by 0x4A29AAD: __fopen_internal (iofopen.c:65)
==347==    by 0x4A29AAD: fopen@@GLIBC_2.2.5 (iofopen.c:86)
==347==    by 0x401B6E: load (dictionary.c:83)
==347==    by 0x4012CE: main (speller.c:40)
==347== 
==347== LEAK SUMMARY:
==347==    definitely lost: 0 bytes in 0 blocks
==347==    indirectly lost: 0 bytes in 0 blocks
==347==      possibly lost: 0 bytes in 0 blocks
==347==    still reachable: 472 bytes in 1 blocks
==347==         suppressed: 0 bytes in 0 blocks
==347== 
==347== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

This seems cryptic to me and I'm hoping someone can help explain and help me fix my problem (and Help50 doesn't have any advice).这对我来说似乎很神秘,我希望有人可以帮助解释并帮助我解决我的问题(Help50 没有任何建议)。

Here is my actual code (please remember there is a second document with a main function that actually utilizes the functions provided, so it is ok for example that the functions seem to not be in proper order).这是我的实际代码(请记住,还有一个带有主要功能的第二个文档,它实际使用了所提供的功能,因此可以,例如,这些功能似乎没有按正确顺序排列)。

// Implements a dictionary's functionality

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

#include "dictionary.h"

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

// Number of buckets in hash table
const unsigned int N = 1000;

// Hash table
node *table[N];

// Dictionary size
int dictionary_size = 0;

// Returns true if word is in dictionary, else false
bool check(const char *word)
{
    // TODO #4!
    
    // make lowercase copy of word
    char copy[strlen(word) + 1];
    for (int i = 0; word[i]; i++)
    {
        copy[i] = tolower(word[i]);
    }
    copy[strlen(word)] = '\0';
    
    // get hash value
    int h = hash(copy);

    // use hash value to see if word is in bucket
    if (table[h] != NULL)
    {
        node *temp = table[h];
        
        while (temp != NULL)
        {
            if (strcmp(temp->word, copy) == 0)
            {
                return true;
            }
            
            temp = temp->next;
        }
    }
    
    return false;
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    // TODO #2
    // source: https://www.reddit.com/r/cs50/comments/1x6vc8/pset6_trie_vs_hashtable/cf9189q/
    // I used this source because I had trouble understanding different variations - this one explained everything well.
    // I modified it slightly to fit my needs
    unsigned int h = 0;
    for (int i = 0; i < strlen(word); i++)
    {
        h = (h << 2) ^ word[i];
    }
    return h % N;
}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
    // TODO #1!
    // open dictionary file
    FILE *file = fopen(dictionary, "r");
    if (file == NULL)
    {
        return false;
    }
    
    // read strings from file one at a time
    char word[LENGTH + 1];
    while (fscanf(file, "%s", word) != EOF)
    {
        node *n = malloc(sizeof(node));
        if (n == NULL)
        {
            return false;
        }
        
        // place word into node
        strcpy(n->word, word);
        
        // use hash function to take string and return an index
        int h = hash(word);

        // make the current node point to the bucket we want
        n->next = table[h];
        
        // make the bucket start now with the current node
        table[h] = n;
        
        //count number of words loaded
        dictionary_size++;
    }

    return true;
}

// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
    // TODO #3!
    return dictionary_size;
}

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

In the same way that we must free every pointer that we malloc , we must fclose every FILE* that we fopen .就像我们必须free malloc每个指针一样,我们必须fclose我们fopen每个FILE*

Your problem stems from this line:你的问题源于这一行:

FILE *file = fopen(dictionary, "r");

which has no corresponding fclose(file) call.它没有相应的fclose(file)调用。 Add this to the end of your loads function, before the return.在返回之前,将此添加到loads函数的末尾。

Valgrind can give very useful information for debugging (especially when your code is compiled with -g for debugging information), like in this excerpt from your question: Valgrind 可以为调试提供非常有用的信息(尤其是当您的代码使用-g编译以获取调试信息时),例如您问题的摘录:

==347== 472 bytes in 1 blocks are still reachable in loss record 1 of 1
==347==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==347==    by 0x4A29AAD: __fopen_internal (iofopen.c:65)
==347==    by 0x4A29AAD: fopen@@GLIBC_2.2.5 (iofopen.c:86)
==347==    by 0x401B6E: load (dictionary.c:83)
==347==    by 0x4012CE: main (speller.c:40)

Valgrind is giving you the stack trace that allocated the memory that ended up leaking - you can see that the last line in your own code is dictionary.c:83 which is the line that calls fopen. Valgrind 为您提供分配了最终泄漏的内存的堆栈跟踪 - 您可以看到您自己代码中的最后一行是dictionary.c:83 ,它是调用 fopen 的行。

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

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