使用sprintf生成文件名时buffer数组值意外变更的技术问询
问题分析与解决方案
嘿,我立刻就发现了导致你buffer被篡改的核心问题——你的name数组长度严重不足!
为什么buffer会被修改?
你定义了char name[1];,但sprintf(name, "%03i.jpg", pictures)生成的字符串至少需要8个字节的空间:比如000.jpg是7个可见字符,再加上C字符串必须的终止符\0,总共需要8字节。
sprintf不会检查目标数组的大小,它会直接往内存里写数据。当name的空间不够时,溢出的字节就会覆盖栈上相邻的变量。刚好你的buffer数组和name在栈上是相邻的(栈内存通常是向下增长的),所以溢出的字符串就直接把buffer的前几个字节给改写了——这就是为什么调用sprintf后buffer的前四个字节变成了0x30('0')、0x30('0')、0x2e('.')、0x6a('j'),刚好对应00.jpg的前几个字符。
修复步骤
扩大
name数组的容量:
把char name[1];改成足够容纳文件名的大小,比如char name[16];(足够应对更长的文件名,留有余量更安全)。补充文件操作的错误检查:
你的代码没有检查fopen是否成功(比如输入文件不存在的情况),也没有检查fread的返回值,这会导致程序在异常情况下崩溃。建议加上这些判断。优化循环逻辑:
原循环条件while (pictures < 50)不够严谨,应该结合fread的返回值判断是否读到文件末尾,避免无效循环。
修正后的代码
#include <stdio.h> #include <stdint.h> typedef uint8_t BYTE; int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s <input file>\n", argv[0]); return 1; } BYTE buffer[512]; // 扩大name数组容量,足够容纳"%03i.jpg"格式的字符串 char name[16]; int pictures = 0; char *infile = argv[1]; FILE *card = fopen(infile, "r"); if (card == NULL) { printf("Could not open file %s\n", infile); return 1; } FILE *imagen = NULL; size_t bytes_read; while ((bytes_read = fread(buffer, sizeof(BYTE), 512, card)) > 0 && pictures < 50) { // 检查是否是JPEG的起始标志 if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0) { if (pictures > 0) { fclose(imagen); } // 生成文件名(更安全的方式可以用snprintf) sprintf(name, "%03i.jpg", pictures); imagen = fopen(name, "w"); if (imagen == NULL) { printf("Could not create file %s\n", name); fclose(card); return 1; } pictures++; } // 如果已经打开了图片文件,写入当前buffer if (imagen != NULL) { fwrite(buffer, sizeof(BYTE), bytes_read, imagen); } } // 关闭最后一个打开的图片文件 if (imagen != NULL) { fclose(imagen); } fclose(card); return 0; }
额外说明
- 更安全的字符串格式化可以用
snprintf,它能指定最大写入字节数彻底避免溢出:snprintf(name, sizeof(name), "%03i.jpg", pictures); - 注意
fwrite的时候要写入实际读到的字节数(bytes_read),而不是固定的512,因为文件最后一段可能不足512字节。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

