C语言读取二进制文件做十六进制转储时输出带ffffff前缀原因
Game Boy存档二进制读取异常问题
问题背景
近期在尝试进行Game Boy存档修改,正在学习C语言环境下的二进制文件读取方法。已知char类型长度为1字节,因此编写如下代码读取文件字节内容:
#include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> int main () { FILE *fp = NULL; char buffer[100]; int filesize; fp = fopen("save file.srm", "r+"); // 打开存档文件 if(fp == NULL) printf("File loading error number %i\n", errno); fseek(fp, 0L, SEEK_END); // 指针移动到文件末尾获取文件大小 filesize = ftell(fp); printf("Filesize is %i bytes\n",filesize); fseek(fp, 0, SEEK_SET); // 将读指针重置到文件开头 fread(buffer, sizeof buffer, 1, fp); // 读取内容到缓冲区 for(int i=70;i<80;i++) printf("%x\n", buffer[i]); // 打印指定区间字节 fclose(fp); return(0); }
实际运行输出
Filesize is 32768 bytes ffffff89 66 ffffff85 2 2 ffffff8b 44 ffffff83 c 0
问题描述
预期读取到的单字节数值最大为0xff,即每行输出两位十六进制数,但实际输出中出现了ffffff89、ffffff8b这类超出单字节范围的数值,需要明确问题产生的具体原因。
问题根因
出现该现象是两个C语言规则共同作用的结果:
char类型的符号性由编译器实现决定:在绝大多数x86、x86_64架构的常见编译器配置下,char默认是有符号类型,取值范围为-128~127。当读取到的字节值大于等于0x80(十进制128)时,存入有符号char变量会被识别为负数。- 可变参数函数的整型提升规则:向
printf这类支持可变参数的函数传入char类型值时,char会被自动提升为int类型。有符号类型的负值在做类型提升时会执行符号位扩展:例如字节值0x89存入有符号char后为-119,符号位扩展为32位int后值为0xffffff89,用%x按十六进制输出时就会显示前缀的多个f。
另外代码还存在一个隐藏问题:打开二进制文件使用了文本模式"r+",在Windows等平台下文本模式会自动对换行、特殊EOF字符做转义处理,可能导致读取到的字节和文件实际内容不一致。
修正方法
- 将缓冲区类型从
char改为unsigned char,明确使用无符号类型存储原始字节,从根源避免符号位扩展问题:unsigned char buffer[100]; - 二进制文件打开时使用二进制读写模式,将fopen的模式参数从
"r+"改为"rb+" - 如果需要固定输出两位十六进制数,可以将打印格式符从
%x改为%02x,不足两位时自动前置补0。
内容的提问来源于stack exchange,提问作者Jojo
相关产品推荐
相关产品推荐

