(CS50 / pset5 / speller)根据check50和valgrind,我遇到内存泄漏问题。我想我使用的是没有值的变量

时间:2020-06-28 21:36:42

标签: c memory-management memory-leaks valgrind cs50

根据check50和valgrind的说法,我有内存泄漏问题。看来我正在尝试使用可能没有值的变量。根据check50,其他所有内容均正常运行,唯一的问题是内存。这是valgrind的错误消息:


==3243== Conditional jump or move depends on uninitialised value(s)
==3243==    at 0x520A60F: tolower (ctype.c:46)
==3243==    by 0x4010CD: check (dictionary.c:36)
==3243==    by 0x400CD9: main (speller.c:112)
==3243==  Uninitialised value was created by a stack allocation
==3243==    at 0x4008E4: main (speller.c:21)
==3243== 
==3243== Use of uninitialised value of size 8
==3243==    at 0x520A623: tolower (ctype.c:46)
==3243==    by 0x4010CD: check (dictionary.c:36)
==3243==    by 0x400CD9: main (speller.c:112)
==3243==  Uninitialised value was created by a stack allocation
==3243==    at 0x4008E4: main (speller.c:21)
==3243== 

WORDS MISSPELLED:     0
WORDS IN DICTIONARY:  143091
WORDS IN TEXT:        6
TIME IN load:         1.46
TIME IN check:        0.00
TIME IN size:         0.00
TIME IN unload:       0.21
TIME IN TOTAL:        1.67

==3243== 
==3243== HEAP SUMMARY:
==3243==     in use at exit: 0 bytes in 0 blocks
==3243==   total heap usage: 143,097 allocs, 143,097 frees, 8,023,462 bytes allocated
==3243== 
==3243== All heap blocks were freed -- no leaks are possible
==3243== 
==3243== For counts of detected and suppressed errors, rerun with: -v
==3243== ERROR SUMMARY: 492 errors from 2 contexts (suppressed: 0 from 0)

Asking for help...

==3243== Conditional jump or move depends on uninitialised value(s)

Looks like you're trying to use a variable that might not have a value? Take a closer look at line 36 of dictionary.c.

这是我的代码(如果您能提供帮助,我会很乐意;)

// Implements a dictionary's functionality

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

#include "dictionary.h"

#define HASHTABLE_SIZE 65536

// 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 = HASHTABLE_SIZE;

// Hash table
node *table[N];
unsigned int totalWords = 0;

// Returns true if word is in dictionary else false
bool check(const char *word)
{
    //Initialize lower case word
    char lcword[LENGTH + 1];

    for (int i = 0; i < LENGTH + 1; i++)
        lcword[i] = tolower(word[i]);


    node *cursor = table[hash(lcword)];

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

        cursor = cursor->next;
    }

    return false;
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    //https://www.reddit.com/r/cs50/comments/1x6vc8/pset6_trie_vs_hashtable/cf9nlkn/
    unsigned int hash_value = 0;

    for (int i = 0, n = strlen(word); i < n; i++)
        hash_value = (hash_value << 2) ^ word[i];

    return hash_value % HASHTABLE_SIZE;
}

// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{

    FILE *dicfile = fopen(dictionary, "r");
    char *word = malloc(LENGTH + 1);

    //Check if word is null
    if (word == NULL)
        return false;

    //Check if the fopen function opened a not NULL file
    if (dicfile == NULL)
       return false;

    //Iterate over dictionary until fscanf return EOF (meaning it's the end of the file)
    while (fscanf(dicfile, "%s", word) != EOF)
    {
        //Create a node to store the current word
        node *new_node = malloc(sizeof(node));

        if (new_node == NULL)
            return false;

        //Copy the new_node's word into the current word
        strcpy(new_node->word, word);

        //Get the index (hash the current word and store it in n)
        int n = hash(new_node->word);

        //Insert the new_node into the linked list
        new_node->next = table[n];
        table[n] = new_node;

        //Add to the total number of words in the text file
        totalWords++;
    }

    fclose(dicfile);
    free(word);
    return true;
}

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

// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{

    for (int i = 0; i < HASHTABLE_SIZE; i++)
    {
        node* cursor = table[i];
        node* tmp;

        while (cursor != NULL)
        {
            tmp = cursor;
            cursor = cursor->next;
            free(tmp);
        }
        free(cursor);
    }
    return true;
}

如果您想知道,我的代码的第36行就是这样的:lcword[i] = tolower(word[i]);

谢谢!

1 个答案:

答案 0 :(得分:1)

关于:

for (int i = 0; i < LENGTH + 1; i++)

在C中,数组的有效索引范围为0...(number of elements in array-1 ),请注意-1

注意:宏LENGTH在发布的代码中没有定义。

函数check()在发布的代码中实现,但从未调用过。

关于:

for ( int i = 0, n = strlen(word); i < n; i++)

函数:strlen()返回一个size_t,因此该语句应为:

for ( size_t i = 0, n = strlen(word); i < n; i++ )

函数size()已实现但从未调用。

包含不使用那些内容的头文件是非常糟糕的编程习惯。例如:

#include <strings.h>

关于;

if (dicfile == NULL)
   return false;

这应该紧随其后;

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

,并且应包含以下语句:

perror( "fopen to read dictionary file failed" );

关于:

while (fscanf(dicfile, "%s", word) != EOF)

还有其他原因导致无法调用fscanf()。应该检查是否成功。

%s输入格式转换说明符可以输入比数组word()中更多的字符。同样,该说明符始终将NUL字节附加到输入。为避免任何缓冲区溢出问题,请始终使用小于缓冲区长度1的修饰符。建议:

while(fscanf(dicfile,“%” LENGTH“ s”,单词)== 1)

关于您的内存泄漏问题:

发布的代码在table[]中修改指针(通过覆盖指针),而无需先检查那里是否已有活动条目。如果该表的“散列”索引处已经有一个活动条目,则新条目必须在当前条目之后(可以在之前链接)链接,并在同一索引中table[]

当然,当覆盖活动条目时,结果是内存泄漏。