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

CS50 Week4 Recover程序Valgrind堆内存仍可达问题求助

CS50 Week4 Recover作业:Valgrind堆内存仍可达问题修复

问题概述

编写的JPG图像恢复程序功能正常,但Valgrind检测显示存在472字节的堆内存仍可达,该内存来自fopen调用分配的FILE结构体资源。

原代码

// Include necessary libraries
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

// Constants
typedef uint8_t BYTE;
int const BLOCK_SIZE = 512;

// Main function
int main(int argc, char *argv[])
{
    // Check for usage
    if (argc != 2)
    {
        printf("Usage: ./recover file.raw");
        return 1;
    }

    // Open file
    FILE *raw_file = fopen(argv[1], "r");

    // If file could not be opened warn the user
    if (raw_file == NULL)
    {
        printf("File could not be opened");
        return 1;
    }

    // Generate jpg
    // Create counter for name of jpg
    unsigned int img_id = 0;

    // Create block for storing the blocks of read data
    BYTE block[BLOCK_SIZE];

    // Create the file pointer for the generated jpgs
    FILE *jpg_file = jpg_file = fopen("000.jpg", "w");

    // Loop thru all blocks
    while (fread(block, 1, BLOCK_SIZE, raw_file) == BLOCK_SIZE)
    {
        // Run if block start is jpg header
        if (block[0] == 0xff && block[1] == 0xd8 && block[2] == 0xff && (block[3] & 0xf0) == 0xe0)
        {
            // Close only when a file has been opened
            if (jpg_file != NULL)
            {
                fclose(jpg_file);
            }

            //Generate the filename to follow the pattern ###.jpg where ###
            //is a 3 digit number that is the id (the nth image) of the image
            char filename[8];
            sprintf(filename, "%03i.jpg", img_id);

            // Create a jpg in base of the filename
            jpg_file = fopen(filename, "w");

            // Increase img id
            img_id++;

            // If file could not be created warn the user
            if (raw_file == NULL)
            {
                printf("File could not be created");
                return 1;
            }
        }
        // If a file has already been opened write the data to it
        if (jpg_file != NULL)
        {
            fwrite(block, 1, BLOCK_SIZE, jpg_file);
        }
    }

    // Close file to prevent memory leaks
    fclose(raw_file);
}

Valgrind检测结果

==7293== Memcheck, a memory error detector
==7293== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7293== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==7293== Command: ./recover card.raw
==7293== 
==7293== 
==7293== HEAP SUMMARY:
==7293==     in use at exit: 472 bytes in 1 blocks
==7293==   total heap usage: 104 allocs, 103 frees, 237,536 bytes allocated
==7293== 
==7293== 472 bytes in 1 blocks are still reachable in loss record 1 of 1
==7293==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==7293==    by 0x4A086CD: __fopen_internal (iofopen.c:65)
==7293==    by 0x4A086CD: fopen@@GLIBC_2.2.5 (iofopen.c:86)
==7293==    by 0x1092EB: main (recover.c:58)
==7293== 
==7293== LEAK SUMMARY:
==7293==    definitely lost: 0 bytes in 0 blocks
==7293==    indirectly lost: 0 bytes in 0 blocks
==7293==      possibly lost: 0 bytes in 0 blocks
==7293==    still reachable: 472 bytes in 1 blocks
==7293==         suppressed: 0 bytes in 0 blocks
==7293== 
==7293== For lists of detected and suppressed errors, rerun with: -s
==7293== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

问题分析

  1. 未关闭最后一个JPG文件:循环结束后,程序仅关闭了raw_file,但最后一个打开的jpg_file文件指针未调用fclose,导致其对应的堆内存(FILE结构体)无法被回收,表现为"仍可达"。
  2. 提前创建空JPG文件:初始化时直接打开000.jpg,若第一个JPG块不在文件起始位置,会导致该文件写入无效数据。
  3. 错误的文件创建检查:判断JPG文件是否创建成功时,错误使用raw_file == NULL,实际应检查jpg_file == NULL。

修复方案

  1. 将jpg_file初始化为NULL,而非提前打开文件。
  2. 循环结束后,检查jpg_file是否非空,调用fclose关闭它。
  3. 修正JPG文件创建后的错误判断条件。

修复后的代码

// Include necessary libraries
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

// Constants
typedef uint8_t BYTE;
int const BLOCK_SIZE = 512;

// Main function
int main(int argc, char *argv[])
{
    // Check for usage
    if (argc != 2)
    {
        printf("Usage: ./recover file.raw");
        return 1;
    }

    // Open file
    FILE *raw_file = fopen(argv[1], "r");

    // If file could not be opened warn the user
    if (raw_file == NULL)
    {
        printf("File could not be opened");
        return 1;
    }

    // Generate jpg
    // Create counter for name of jpg
    unsigned int img_id = 0;

    // Create block for storing the blocks of read data
    BYTE block[BLOCK_SIZE];

    // Initialize jpg file pointer to NULL
    FILE *jpg_file = NULL;

    // Loop thru all blocks
    while (fread(block, 1, BLOCK_SIZE, raw_file) == BLOCK_SIZE)
    {
        // Run if block start is jpg header
        if (block[0] == 0xff && block[1] == 0xd8 && block[2] == 0xff && (block[3] & 0xf0) == 0xe0)
        {
            // Close only when a file has been opened
            if (jpg_file != NULL)
            {
                fclose(jpg_file);
            }

            //Generate the filename to follow the pattern ###.jpg where ###
            //is a 3 digit number that is the id (the nth image) of the image
            char filename[8];
            sprintf(filename, "%03i.jpg", img_id);

            // Create a jpg in base of the filename
            jpg_file = fopen(filename, "w");

            // Increase img id
            img_id++;

            // If file could not be created warn the user
            if (jpg_file == NULL)
            {
                printf("File could not be created");
                // Close raw file before exiting
                fclose(raw_file);
                return 1;
            }
        }
        // If a file has already been opened write the data to it
        if (jpg_file != NULL)
        {
            fwrite(block, 1, BLOCK_SIZE, jpg_file);
        }
    }

    // Close the last jpg file if it's open
    if (jpg_file != NULL)
    {
        fclose(jpg_file);
    }

    // Close raw file
    fclose(raw_file);
    
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 07:25:40