使用mmap()读取时ntohl()返回0的技术求助
嘿,这个问题我之前帮朋友排查过类似的情况,咱们一步步拆解根源,找到解决办法~
首先得明确ntohs()和ntohl()的核心区别:前者是把16位网络字节序(大端)转成主机字节序,后者处理的是32位。你说同一位置调用两个函数结果天差地别,大概率是以下几个原因之一:
1. 数据长度不匹配,读取了无效的额外字节
如果文件里这个位置本来就只有16位的有效数据,那用ntohl()读取32位时,会把后面紧跟的16位(可能是文件填充的0)一起读进来。比如原16位数据是0x1234(大端),后面16位是0x0000,那读取的32位数据就是0x00001234,经过ntohl()转换后(假设你是小端主机),会变成0x34120000——但如果你误以为这个值是0,可能是输出时只看了高32位?不对,更可能是下面的问题。
2. 结构体类型不匹配+内存对齐坑
看你代码里定义了blockInfo结构体,用了long类型——这可是个大坑!long的长度在不同平台上不一样:32位系统是4字节,64位系统是8字节。而且编译器会给结构体自动添加对齐填充字节,导致结构体的内存布局和文件里的二进制结构完全对不上。
举个典型的例子:
- 文件里
blockSize是32位(4字节)的大端数据0x12345678 - 你的结构体里
blockSize是long(64位),在64位小端系统上,读取时会把文件里的4个有效字节+后面4个0字节拼成一个64位值:0x7856341200000000 - 你用
ntohl()处理这个值时,ntohl()只接受uint32_t类型,会自动截断高32位,结果就变成了0x00000000——这就是你看到的返回0! - 而用
ntohs()读取前16位时,刚好命中有效数据,所以能正常返回。
3. 指针强制转换的对齐问题
如果你的映射指针是char*或uint8_t*,直接强制转成uint32_t*时,可能会碰到内存对齐错误——有些CPU不允许非对齐的4字节访问,会返回错误值(比如0)。而uint16_t*的对齐要求更低,所以能正常读取。
解决办法
针对这些问题,给你几个具体的修复步骤:
① 改用固定宽度的整数类型
把结构体里的long换成<cstdint>里的固定宽度类型,比如uint32_t(4字节)、uint16_t(2字节),这样不管什么平台,类型长度都是确定的:
#include <cstdint> struct blockInfo { uint32_t blockSize = 0; uint32_t blockCount = 0; // 其他字段... } __attribute__((packed)); // GCC/Clang下禁用自动对齐填充,MSVC用#pragma pack(push,1)
加上__attribute__((packed))是为了让结构体的内存布局和文件完全一致,避免编译器自动加填充字节。
② 安全读取数据,避免对齐问题
别直接强制转换指针,用memcpy把字节复制到对应的变量里,再做字节序转换:
// 假设img_ptr是mmap返回的uint8_t*指针 blockInfo info; // 读取blockSize memcpy(&info.blockSize, img_ptr, sizeof(info.blockSize)); info.blockSize = ntohl(info.blockSize); // 仅当文件是大端字节序时用 // 读取blockCount(偏移blockSize的长度) memcpy(&info.blockCount, img_ptr + sizeof(info.blockSize), sizeof(info.blockCount)); info.blockCount = ntohl(info.blockCount);
③ 先验证原始字节
如果还是有问题,先打印出读取位置的原始字节,确认数据是否符合预期:
// 打印前4字节的原始十六进制值 printf("Raw bytes at target position: 0x%02X%02X%02X%02X\n", img_ptr[0], img_ptr[1], img_ptr[2], img_ptr[3]);
这样就能一眼看出文件里的实际数据是不是你想要的,排除字节序或位置偏移的问题。
内容的提问来源于stack exchange,提问作者JoryAnderson

