CS50 Pset4 recover程序未通过check50检测求助
SD卡图像恢复程序调试求助
我写了个从数码相机SD卡恢复删除图像的程序,运行check50时最后三项检测没通过,000.jpg、中间图像和049.jpg都恢复不出来,还报程序超时等待退出,用调试工具没解决问题,求技术帮助。
我的代码
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> // Buffer to store card data // recover deleted images int main(int argc, char *argv[]) { // count is 2 if(argc < 2) { printf("usage: ./recover image.raw\n"); return 1; } // Read file char *file = argv[1]; FILE *card = fopen(file, "r"); unsigned char* buffer = malloc(512); // Check that file is valid if (fopen(file, "r") == NULL) { printf("could not open %s.\n", file); return 1; } char* filename = malloc(3 * sizeof(int)); //JPGs created int img_count = 0; int test_counter = 0; FILE *img = NULL; // Read 512 chars while (test_counter == 0) { fread(buffer, sizeof(char), 512, card); // Detect JPEG if(buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && buffer[3] && 0xf0 == 0xe0) { // Write JPEG sprintf(filename, "%0x3i.jpg", img_count); img = fopen(filename, "w"); img_count++; while (true) { fread(buffer, sizeof(char), 512, card); if(buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && buffer[3] && 0xf0 == 0xe0) { sprintf(filename, "%0x3i.jpg", img_count); img = fopen(filename, "a"); img_count++; test_counter++; fwrite(buffer, sizeof(char), 512, img); break; } else { fwrite(buffer, sizeof(char), 512, img); break; } } while (fread(buffer, sizeof(char), 512, card)!= 0) if(buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && buffer[3] && 0xf0 == 0xe0) { // Pointer for JPG created fclose(img); sprintf(filename, "%0x3i.jpg", img_count); img = fopen(filename, "a"); img_count++; } fwrite(buffer, sizeof(char), 512, img); } } free(buffer); fclose(img); return 0; }
问题分析与修复方案
核心错误点
循环逻辑完全混乱
- 外层
while(test_counter == 0)只会执行一次,无法遍历整个raw文件,导致大部分图像没被处理。 - 内部嵌套的
while(true)每次只读取一个512字节块就break,完全没实现“持续写入直到下一个JPEG头出现”的逻辑。
- 外层
JPEG头检测条件错误
原代码里buffer[3] && 0xf0 == 0xe0是逻辑错误,正确的JPEG起始第四字节检测应该是(buffer[3] & 0xf0) == 0xe0——需要用位运算判断高四位是否为0xe。文件名格式化错误
sprintf(filename, "%0x3i.jpg", img_count)里的格式符错误:%0x是十六进制输出,这里需要三位十进制的文件名(比如000.jpg),应该用%03d。文件操作错误
- 新建JPEG文件时用
"a"追加模式是错的,应该用"w"覆盖模式(因为是新文件)。 - 重复
fopen且未关闭旧文件,会导致文件资源泄漏,还可能写入混乱。 - 原代码重复打开了两次
card文件,应该直接检查第一次fopen返回的card指针是否为NULL。
- 新建JPEG文件时用
未处理文件读取边界
没有检查fread的返回值,当读到文件末尾(返回值小于512)时,依然按512字节写入,会导致垃圾数据,甚至程序崩溃。
修正后的代码
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> int main(int argc, char *argv[]) { if (argc != 2) { printf("usage: ./recover image.raw\n"); return 1; } FILE *card = fopen(argv[1], "r"); if (card == NULL) { printf("could not open %s.\n", argv[1]); return 1; } unsigned char *buffer = malloc(512); if (buffer == NULL) { fclose(card); printf("memory allocation failed.\n"); return 1; } char filename[8]; // 足够存"000.jpg" + '\0' FILE *img = NULL; int img_count = 0; size_t bytes_read; // 遍历整个raw文件 while ((bytes_read = fread(buffer, sizeof(unsigned char), 512, card)) > 0) { // 检测JPEG起始头 if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0) { // 如果之前有打开的图像文件,先关闭 if (img != NULL) { fclose(img); } // 生成新文件名并打开文件 sprintf(filename, "%03d.jpg", img_count); img = fopen(filename, "w"); if (img == NULL) { fclose(card); free(buffer); printf("could not create %s.\n", filename); return 1; } img_count++; } // 如果当前有打开的图像文件,写入数据 if (img != NULL) { fwrite(buffer, sizeof(unsigned char), bytes_read, img); } } // 关闭剩余的文件和释放内存 if (img != NULL) { fclose(img); } fclose(card); free(buffer); return 0; }
修正说明
- 简化了循环逻辑:用一个
while循环遍历整个raw文件,每次读取512字节块。 - 修复了JPEG头检测的位运算逻辑。
- 文件名格式改为
%03d,确保生成三位十进制文件名。 - 每次检测到新JPEG头时,先关闭之前的图像文件,再创建新文件。
- 处理了
fread的返回值,写入时按实际读取的字节数写入,避免文件末尾的垃圾数据。 - 增加了内存分配失败的判断,提升程序健壮性。
内容的提问来源于stack exchange,提问作者Noddin Leigh
相关产品推荐
相关产品推荐

