C++自定义BinaryReader读取TTF文件时数值读取结果异常问题
问题根因
你遇到的问题由两个原因共同导致:
- 核心原因是字节序(端序)不匹配:TTF格式规范规定所有多字节整数字段均采用大端序(Big-Endian,高位字节存储在低偏移位置),而x86、x86_64等绝大多数消费级CPU采用小端序(Little-Endian,低位字节存储在低地址)。你直接将文件二进制内容读取到
uint16_t/uint32_t类型变量的内存空间中,没有做端序转换,得到的数值自然是错误的。
你提到的scalar type字段期望值为65536,对应十六进制值为0x00010000,在TTF文件中存储的字节顺序为00 01 00 00。小端序CPU直接读取这4字节解析为uint32_t时,会将第一个字节作为数值最低位,最终解析出0x00000100即256,和你实际输出的结果完全吻合。 - 你将
fi.read的读取长度改为3621这类随机值仍输出256,是因为该操作属于典型的栈缓冲区溢出未定义行为:uint32_t类型变量x仅占4字节内存,你要求read写入3621字节,会直接覆盖x变量地址后方的栈内存(包括其他局部变量、函数返回地址等),但x本身所在的4字节内存恰好被写入了和读取4字节时完全相同的内容,因此返回值没有变化。这种操作轻则导致程序随机崩溃、输出乱码,重则可被利用执行恶意代码,绝对不要在实际代码中使用。
修复方案
不要直接将多字节内容整块读取到整型变量内存中,改为逐字节读取后按大端序手动拼接数值,该写法不依赖运行环境的CPU端序,也不会出现内存对齐、越界问题。
修复后的多字节读取方法实现如下:
// 原getUInt8实现无需修改 int getUInt8(){ fi.seekg(position, ios::beg); uint8_t x; fi.read((char*)&x, sizeof(x)); position+=sizeof(x); return (int)x; } int getUInt16(){ uint16_t res = 0; // 大端序第一个字节是高位,左移8位 res |= (uint16_t)getUInt8() << 8; res |= (uint16_t)getUInt8(); return (int)res; } int getUInt32(){ uint32_t res = 0; // 大端序按从高到低顺序拼接每个字节 res |= (uint32_t)getUInt8() << 24; res |= (uint32_t)getUInt8() << 16; res |= (uint32_t)getUInt8() << 8; res |= (uint32_t)getUInt8(); return (int)res; }
替换原有实现后,读取到的scalar type、numTables等字段值就会和TTF规范定义的预期值一致。
内容的提问来源于stack exchange,提问作者Luna Le Tuna
相关产品推荐
相关产品推荐

