bitmapfileheader和bitmapinfoheader必须用#pragma pack(2)保证2字节对齐,否则字段错位;bfoffbits指像素数据起始偏移(含调色板),需按行4字节对齐重算;biheight为负表示top-down存储,读取时须行序反转。

BITMAPFILEHEADER 和 BITMAPINFOHEADER 的内存布局必须严格对齐
Windows 位图文件头不是普通结构体,BITMAPFILEHEADER 和 BITMAPINFOHEADER 依赖 #pragma pack(2)(实际是 2 字节对齐),否则直接 memcpy 到结构体会因编译器自动填充导致字段错位。VC++ 默认结构体按 8 或 16 字节对齐,读出来 bfOffBits 可能指向错误位置,后续解析全崩。
实操建议:
- 用
#pragma pack(push, 2)包裹结构体定义,读取前确保对齐一致 - 不要依赖
sizeof(BITMAPFILEHEADER)做偏移计算——它可能等于 16(对齐后),但真实文件中固定占 14 字节 - 手动校验:读取前 2 字节是否为
'B'和'M',再检查bfSize是否与实际文件大小一致,避免误判非 BMP 数据
从内存 buffer 解析时,bfOffBits 是关键分水岭
bfOffBits 不是“图像数据起始偏移”的绝对值,而是从文件开头到 BITMAPINFOHEADER 之后第一个像素字节的距离。如果位图含调色板(如 8bpp),这个值会大于 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER),差值就是调色板字节数。
常见错误现象:
- 把
bfOffBits当作“信息头结束位置”,直接从那里开始读像素,结果跳过调色板,图像颜色全乱 - 忽略
biCompression:值为BI_BITFIELDS(3)时,bfOffBits后面紧跟着 3 个 DWORD 掩码,不是像素数据 - 没检查
biHeight符号:负值表示倒序存储(top-down BMP),逐行读取时 Y 方向要反转
修改 biWidth / biHeight 后必须重算 bfSize 和 bfOffBits
单纯改 biWidth 或 biHeight 不影响文件头本身大小,但会改变像素数据总字节数和对齐要求,进而影响 bfSize(整个文件大小)和 bfOffBits(像素起始位置)。尤其当新宽度导致每行字节数变化时,Windows 要求每行必须是 4 字节对齐,所以补零量(padding)会变。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
计算逻辑:
- 每行原始字节数 =
((biWidth * biBitCount) + 7) / 8 - 对齐后行字节数 =
((biWidth * biBitCount) + 31) / 32 * 4 -
bfOffBits=sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 调色板字节数 -
bfSize=bfOffBits + 对齐后行字节数 * abs(biHeight)
漏掉 padding 重算,写回内存后用 Windows 看图器打开会提示“无效图像”或直接崩溃。
用 reinterpret_cast 读取像素前先确认 biBitCount 和格式
biBitCount 决定每个像素占多少位,不等于每个像素占多少字节。例如 16bpp 实际是 2 字节,但可能是 5-5-5 或 5-6-5;24bpp 是 3 字节 BGR;32bpp 是 4 字节 BGRA(注意 Alpha 通道是否存在)。直接 reinterpret_cast<uint32_t></uint32_t> 强转 24bpp 数据会越界。
使用场景提醒:
- 处理 24bpp:用
uint8_t*按 BGR 顺序逐字节访问,别用整型指针跨读 - 处理 32bpp:检查
biCompression == BI_RGB还是BI_BITFIELDS,后者需结合掩码解包 - 所有操作前用
IsBadReadPtr()(仅调试)或更安全的边界检查,防止 buffer 长度小于bfSize导致读越界
最易被忽略的是:BMP 像素数据在内存中是 bottom-up 存储(除非 biHeight 为负),而多数图像处理库默认 top-down。不翻转行序就直接送进 OpenGL 或 OpenCV,图像是镜像的。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










