我在退出时使用了字节,但似乎无法确定确切位置 cs50,pset5

问题描述

我刚刚完成了cs50 pset 5 speller代码。它不是完美而高效的,但我首先要使其工作。该代码对给定词典中的文本进行拼写检查。该程序将按预期运行。我在上面使用valgrind来检查内存泄漏,并显示以下内容

==12518== Memcheck,a memory error detector
==12518== Copyright (C) 2002-2017,and GNU GPL'd,by Julian Seward et al.
==12518== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==12518== Command: ./speller texts/lalaland.txt
==12518== 
==12518== Conditional jump or move depends on uninitialised value(s)
==12518==    at 0x524A071: _IO_vfscanf (vfscanf.c:1021)
==12518==    by 0x52562E5: __isoc99_fscanf (isoc99_fscanf.c:34)
==12518==    by 0x4011FC: load (dictionary.c:67)
==12518==    by 0x4009B4: main (speller.c:40)
==12518==  Uninitialised value was created by a stack allocation
==12518==    at 0x401194: load (dictionary.c:59)
==12518== 
==12518== Use of uninitialised value of size 8
==12518==    at 0x5246FDD: _IO_vfscanf (vfscanf.c:1103)
==12518==    by 0x52562E5: __isoc99_fscanf (isoc99_fscanf.c:34)
==12518==    by 0x4011FC: load (dictionary.c:67)
==12518==    by 0x4009B4: main (speller.c:40)
==12518==  Uninitialised value was created by a stack allocation
==12518==    at 0x401194: load (dictionary.c:59)
==12518== 
==12518== 
==12518== Process terminating with default action of signal 11 (SIGSEGV)
==12518==  Bad permissions for mapped region at address 0x51DF0E8
==12518==    at 0x5246FDD: _IO_vfscanf (vfscanf.c:1103)
==12518==    by 0x52562E5: __isoc99_fscanf (isoc99_fscanf.c:34)
==12518==    by 0x4011FC: load (dictionary.c:67)
==12518==    by 0x4009B4: main (speller.c:40)
==12518== 
==12518== HEAP SUMMARY:
==12518==     in use at exit: 552 bytes in 1 blocks
==12518==   total heap usage: 2 allocs,1 frees,4,648 bytes allocated
==12518== 
==12518== 552 bytes in 1 blocks are still reachable in loss record 1 of 1
==12518==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12518==    by 0x5258E49: __fopen_internal (iofopen.c:65)
==12518==    by 0x5258E49: fopen@@GLIBC_2.2.5 (iofopen.c:89)
==12518==    by 0x4011B1: load (dictionary.c:60)
==12518==    by 0x4009B4: main (speller.c:40)
==12518== 
==12518== LEAK SUMMARY:
==12518==    definitely lost: 0 bytes in 0 blocks
==12518==    indirectly lost: 0 bytes in 0 blocks
==12518==      possibly lost: 0 bytes in 0 blocks
==12518==    still reachable: 552 bytes in 1 blocks
==12518==         suppressed: 0 bytes in 0 blocks
==12518== 
==12518== For counts of detected and suppressed errors,rerun with: -v
==12518== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
Segmentation fault

我是一名初学者,从中我了解到我没有在某个地方释放552个字节。下面是我的代码:

// Implements a dictionary's functionality
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <strings.h>
#include <stdlib.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 = 5000;

// Hash table
node *table[N];

int words = 0;

// Returns true if word is in dictionary else false
bool check(const char *word)
{
    int x = hash(word);
    node *cursor = table[x];
    while (cursor != NULL)
    {
        if (strcasecmp (cursor->word,word) == 0)
        {
            return true;
        }
        cursor = cursor->next;
    }
    return false;
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    int x = 0;
    for (int i = 0,n = strlen(word); i < n; i++)
    {
        char a;
        a = toupper(word[i]);
        x += a;
    }
    return x;
}

// Loads dictionary into memory,returning true if successful else false
bool load(const char *dictionary)
{
    FILE *file = fopen(dictionary,"r");
    if (file == NULL)
    {
        printf("Dictionary couldn't be opened\n");
        return false;
    }
    char *word[LENGTH + 1];
    while (fscanf(file,"%s",*word) != EOF)
    {
        node *n = malloc(sizeof(node));
        if (n == NULL)
        {
            printf("Out of memory.\n");
            return false;
        }
        strcpy (n->word,*word);
        n->next = NULL;
        int x = hash(*word);
        if (table[x] == NULL)
        {
            table[x] = n;
        }
        else
        {
            n->next = table[x];
            table[x] = n;
        }
        words++;
    }
    fclose(file);
    return true;
}

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

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

有人可以帮助我找出问题所在吗?

另外请注意,我运行check50来检查它是否可以正常运行,但没有通过,我得到了this result。我猜这与内存泄漏有关,但我不确定。

解决方法

仔细查看日志,它们实际上具有非常有用的信息。它告诉我您正在引用一个未初始化的值。实际上,这可能会导致SEGFAULT。这在日志中得到确认:

==12518== Process terminating with default action of signal 11 (SIGSEGV)

让我们看看如何解决这个问题。日志的相关部分是:

==12518== Conditional jump or move depends on uninitialised value(s)
==12518==    at 0x524A071: _IO_vfscanf (vfscanf.c:1021)
==12518==    by 0x52562E5: __isoc99_fscanf (isoc99_fscanf.c:34)
==12518==    by 0x4011FC: load (dictionary.c:67)
==12518==    by 0x4009B4: main (speller.c:40)
==12518==  Uninitialised value was created by a stack allocation
==12518==    at 0x401194: load (dictionary.c:59)

此日志的第一部分是回溯。它显示导致错误的函数调用顺序。在这种情况下,main称为load,后者称为<something>_fscanf。回溯的其余部分用于实现libc的{​​{1}}中的内部调用。这告诉我们错误所在的位置。它还告诉我什么错误是:程序使用值而不初始化它。

现在,让我们看一下fscanf函数中的相关行:

load

其中,我可以看到 while (fscanf(file,"%s",*word) != EOF) { ... } 已通过调用file并检查返回的空值来正确初始化。第二个参数是一个常量字符串。因此,第三个论点是问题。让我们看看它的声明位置。

现在,在上一行中,您看到fopen被声明为word,它是一个字符指针数组。我想您实际上想要的是指向char数组的指针。正确的声明应为char *word[]。在当前形式中,它是一个指针数组。这些指针尚未在此函数中初始化,因此实际上可能包含char word[LENGTH + 1]空指针。此外,由于堆栈重用(请参阅堆栈帧如何工作),此数组可能包含其他随机垃圾,其中可能包括实际的指针(并导致状态损坏)。实际上,这正是发生的情况。让我们回到日志:

LENGTH + 1

未初始化的指针保存着值==12518== Process terminating with default action of signal 11 (SIGSEGV) ==12518== Bad permissions for mapped region at address 0x51DF0E8 ,并且0x51df0e8可能试图写入该地址,由于指针实际上是无效的,因此遇到了分段错误。

最后,让我们看一下日志的最后一部分:

fscanf

这告诉我们一些分配的堆内存没有被释放。这是您的细分错误的结果。 ==12518== HEAP SUMMARY: ==12518== in use at exit: 552 bytes in 1 blocks 已执行,但没有执行相应的malloc,因为程序在两者之间崩溃了。

最后,我想强调指出,读取和解释包含许多有用信息的日志非常有用。此外,我还建议您学习使用free之类的调试器,该调试器将使您能够快速而准确地识别程序中的问题,并且随着程序的增长而变得必不可少。 Bon编码,厨师...

,

希望LENGTHdictionary.h中定义,并且比文件中的任何单词长

char word[LENGTH + 1]; // not char *word[LENGTH + 1];

返回之前请记住close(file)

size_t table_index = x % N; //need table_index between 0 and N-1 

如果更改表的声明方式,请记住在开始时将表归零。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...