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)
问题分析
- 未关闭最后一个JPG文件:循环结束后,程序仅关闭了
raw_file,但最后一个打开的jpg_file文件指针未调用fclose,导致其对应的堆内存(FILE结构体)无法被回收,表现为"仍可达"。 - 提前创建空JPG文件:初始化时直接打开
000.jpg,若第一个JPG块不在文件起始位置,会导致该文件写入无效数据。 - 错误的文件创建检查:判断JPG文件是否创建成功时,错误使用
raw_file == NULL,实际应检查jpg_file == NULL。
修复方案
- 将
jpg_file初始化为NULL,而非提前打开文件。 - 循环结束后,检查
jpg_file是否非空,调用fclose关闭它。 - 修正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
相关产品推荐
相关产品推荐

