CS50 recover作业valgrind内存错误排查及check50本地远程差异问题
CS50 Recover作业内存错误与本地/远程check50差异问题解析
问题概况
- 远程check50:功能测试全部通过,但valgrind检测出多处Invalid write/read内存错误,涉及
recover.c第84、88、102行 - 本地valgrind:运行各类命令均未检测到内存泄漏或错误
- 本地check50:仅通过文件存在和编译检查,其余功能测试因退出码不符(预期1/0,实际127)失败
代码中的内存错误根源
1. 栈上可变长度数组(VLA)溢出
在recoverJPGS函数里,你用uint8_t image[imageSize];在栈上创建了可变长度数组。JPG文件体积可能远超栈的默认容量(通常仅几MB),当imageSize过大时直接引发栈溢出——远程环境的栈限制比本地严格,所以本地没触发但远程被valgrind抓个正着。
2. findJPGS的越界访问风险
size_t numberJpgs = -1;:size_t是无符号类型,-1会被转成极大的正数。如果没找到任何JPG,后续jpgs[numberJpgs].end = jpgStart - 1;会直接越界访问数组,触发Invalid write错误。- 未检查
numberJpgs是否超过传入的数组容量(50):如果找到的JPG数量超过50,会直接往jpgs数组外面写数据,导致内存越界。
3. 二进制文件处理错误
打开输出文件用了"w"模式,处理二进制文件必须用"wb"模式——虽然部分系统兼容,但会导致二进制数据写入时的换行符转换问题,可能破坏JPG文件结构。
4. fread重复调用导致数据丢失
在recoverJPGS的错误处理里,你连续调用了两次fread:第一次失败后,第二次调用会继续读取文件,导致文件指针偏移,后续数据读取完全错误。
5. 文件偏移的类型转换溢出
range.start是size_t类型,转成long int可能溢出(如果文件大小超过long int最大值),应该用fseeko和off_t类型处理大文件偏移。
本地与远程结果差异的原因
1. 环境配置差异
- 栈大小限制:远程CS50环境的栈容量比本地小,栈上VLA的溢出在远程更容易触发。
- 工具版本差异:远程的clang、valgrind版本更严格,能捕捉到本地版本忽略的内存问题。
- 系统架构差异:32位/64位系统的类型长度(如
size_t、long int)不同,类型转换时的溢出问题可能只在某一环境触发。
2. 本地check50退出码127问题
退出码127表示找不到要执行的命令,大概率是本地check50环境配置错误,或者你的./recover可执行文件不在check50的搜索路径里。先直接运行./recover看能否正常执行,再检查check50的本地配置。
代码修正示例
1. 替换VLA为动态内存分配
把栈上的VLA换成malloc分配的堆内存,避免栈溢出:
// 替换uint8_t image[imageSize]; uint8_t *image = malloc(imageSize); if (image == NULL) { printf("malloc failed for image %lu\n", i); return 1; } // 使用完image后记得释放 free(image);
2. 修复findJPGS的无符号类型与越界问题
int findJPGS(FILE *file, struct byteRange *jpgs, size_t size) { uint8_t block[512]; size_t jpgStart = 0; size_t numberJpgs = 0; int found_first = 0; // 标记是否找到第一个JPG // 这里去掉&block,block本身就是指针 while (fread(block, sizeof(uint8_t), 512, file)) { for (size_t i = 0; i < 512; i++) { if (block[i] == 0xff && i + 3 < 512) { if (block[i+1] == 0xd8 && block[i+2] == 0xff && (block[i+3] & 0xf0) == 0xe0) { if (!found_first) { found_first = 1; jpgs[numberJpgs].start = jpgStart; } else { // 检查是否超过数组容量 if (numberJpgs >= size) { printf("Too many JPGs, array full\n"); return numberJpgs; } jpgs[numberJpgs].end = jpgStart - 1; numberJpgs++; jpgs[numberJpgs].start = jpgStart; } } } jpgStart++; } } // 确保找到过JPG才设置end if (found_first) { jpgs[numberJpgs].end = jpgStart - 1; numberJpgs++; } return numberJpgs; }
3. 修复文件模式与fread错误处理
// 打开输出文件用wb模式 FILE *output = fopen(fileName, "wb"); // 修复fread重复调用问题 size_t read_bytes = fread(image, 1, imageSize, file); if (read_bytes != imageSize) { printf("fread image %lu failed: read %lu/%lu bytes\n", i, read_bytes, imageSize); free(image); return 1; }
4. 使用fseeko处理大文件偏移
// 替换fseek为fseeko if (fseeko(file, (off_t)range.start, SEEK_SET) != 0) { printf("fseek image %lu failed\n", i); free(image); return 1; }
内容的提问来源于stack exchange,提问作者Ican'tThinkOfAName
相关产品推荐
相关产品推荐

