You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CS50 Speller项目Valgrind检测内存泄漏求助

CS50 Speller项目内存泄漏问题解决

问题概述

CS50 Speller项目代码无法通过Valgrind测试,存在内存泄漏。Valgrind日志显示有56字节的内存块仍可访问,泄漏点指向代码第96行的node *current_wrd = malloc(sizeof(node));语句。

Valgrind报错日志

running valgrind --show-leak-kinds=all --xml=yes --xml-file=/tmp/tmpx3faci2p -- ./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: 96) 

完整代码

// Implements a dictionary's functionality
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <cs50.h>

#include "dictionary.h"

// 全局变量
unsigned int word_count;
unsigned int hash_num;

// 哈希表节点结构
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
} node;

// 哈希表桶数量
const unsigned int N = 26;

// 哈希表
node *table[N];

// 检查单词是否在字典中
bool check(const char *word)
{
    hash_num = hash(word);

    node* cursor = table[hash_num];

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

        cursor = cursor->next;
    }

    return false;
}

// 哈希函数
unsigned int hash(const char *word)
{
    unsigned long total = 0;

    for(int i = 0; i < strlen(word); i++)
    {
        total+=tolower(word[i]);
    }

    return total % N;
}

// 加载字典到内存
bool load(const char *dictionary)
{
    FILE *source = fopen(dictionary,"r");

    if(source == NULL)
    {
        printf("Could not open the file\n");
        return false;
    }

    char current_word[LENGTH+1];

    while (fscanf(source, "%s", current_word) != EOF)
    {
        node *current_wrd = malloc(sizeof(node));

        if(current_wrd == NULL)
        {
            return false;
        }

        strcpy(current_wrd->word, current_word);
        hash_num = hash(current_word);

        current_wrd->next = table[hash_num];
        table[hash_num] = current_wrd;
        word_count++;
    }

    fclose(source);
    return true;
}

// 返回字典中的单词数量
unsigned int size(void)
{
    if(word_count > 0)
    {
        return word_count;
    }

    return 0;
}

// 释放字典内存
bool unload(void)
{
    for(int i = 0; i < N; i++)
    {
        node *cursor = table[i];

        while(cursor)
        {
            node *tmp = cursor;
            cursor = cursor->next;
            free(tmp);
        }

        if(cursor == NULL)
        {
            free(cursor);
            return true;
        }
    }
    return false;
}

问题定位

内存泄漏的根源在unload函数:

  • 函数在遍历第一个桶(i=0)并释放其链表节点后,直接执行return true,导致后续N-1个桶的节点完全没有被释放。
  • 当字典中的单词分布在多个桶时,未处理的桶内节点会成为"仍可访问"的内存块,触发Valgrind报错。

修复方案

修改unload函数,遍历所有N个桶完成节点释放后,再统一返回true:

bool unload(void)
{
    for(int i = 0; i < N; i++)
    {
        node *cursor = table[i];

        while(cursor)
        {
            node *tmp = cursor;
            cursor = cursor->next;
            free(tmp);
        }
    }
    return true;
}

修复说明

  1. 移除了原函数中处理完单个桶就返回的逻辑,确保所有桶的链表节点都被遍历释放。
  2. 无需额外free(cursor),因为循环结束时cursor已经是NULL,free(NULL)是安全但无意义的操作。
  3. 遍历完所有桶后直接返回true,因为只要进入函数就会完成所有内存释放。

内容的提问来源于stack exchange,提问作者keshika sathsara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 05:07:32