C++读取BMP需先校验文件头:bfType必须为0x4D42("BM"),bfOffBits≥头大小且不超文件长,biBitCount限1/4/8/16/24/32;像素数据自下而上存储、每行4字节对齐、BGR顺序。

如何用 C++ 读取 BMP 文件头并验证有效性
直接读取 BMP 文件前,必须先校验文件头是否合法,否则后续解析会崩溃或读错数据。关键不是“能不能读”,而是“读到的到底是不是真 BMP”。
-
BITMAPFILEHEADER前两个字节必须是0x42 0x4D(即 ASCII "BM"),否则直接返回错误 -
bfOffBits字段决定像素数据起始位置,它必须 ≥sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER),且不能超过文件大小 -
BITMAPINFOHEADER::biBitCount必须是 1、4、8、16、24 或 32,其他值(如 15、30)虽在某些 Windows 版本中被容忍,但标准不支持,fread后可能解码错乱 - 注意字节序:BMP 是小端(little-endian),Windows 系统上结构体直接
fread可用,但跨平台时需手动翻转字段(如biWidth、biHeight)
FILE* fp = fopen("test.bmp", "rb");
if (!fp) { /* 错误处理 */ }
BITMAPFILEHEADER bmfh;
fread(&bmfh, sizeof(bmfh), 1, fp);
if (bmfh.bfType != 0x4D42) { // 'M' 'B',注意小端存储顺序
fclose(fp);
return -1;
}
为什么读出的像素数据上下颠倒、每行末尾有填充字节
BMP 的像素数据从图像左下角开始存储,逐行向上,且每行字节数必须是 4 的倍数——这是 Windows GDI 的硬性要求,不是可选行为。
-
biHeight为正数表示“自下而上”存储;为负数表示“自上而下”(Windows NT+ 支持,但多数生成器仍用正值) - 每行像素字节数 =
((biWidth * biBitCount + 31) / 32) * 4,即向上对齐到 4 字节边界;原始宽度字节数不足时,末尾补0x00 - 如果你按
biWidth * 3直接读 24 位图,遇到宽度为 101 像素时就会越界(实际每行占 304 字节,而非 303) - 图像显示颠倒,是因为你把第 0 行当成了顶部,而它其实是底部;修复方式是按
biHeight倒序拷贝行,或修改渲染逻辑
如何安全读写 24 位真彩色 BMP 的 RGB 像素
24 位 BMP 最常见,但操作时最容易因对齐和顺序栽跟头。不要假设内存布局和磁盘布局一致。
- 计算每行真实字节数:
rowSize = ((width * 24 + 31) / 32) * 4,别用width * 3 - 分配缓冲区时,按
rowSize * abs(height)分配,而不是width * height * 3 - 读取时用
fseek(fp, bmfh.bfOffBits, SEEK_SET)跳过头,再逐行fread(lineBuffer, 1, rowSize, fp) - 写入时,每行写完后若
rowSize > width * 3,需补0到末尾(可用memset填充) - 注意:BMP 存储顺序是 BGR(不是 RGB),即每个像素三字节为
blue、green、red;直接丢给 OpenGL / SDL 显示前必须交换 R/B
int width = bi.biWidth; int height = bi.biHeight; int rowSize = ((width * 24 + 31) / 32) * 4; std::vectorpixelData(rowSize * abs(height)); fseek(fp, bmfh.bfOffBits, SEEK_SET); for (int i = 0; i < abs(height); ++i) { int srcRow = (height > 0) ? (abs(height) - 1 - i) : i; // 自下而上 → 自上而下 fread(pixelData.data() + srcRow * rowSize, 1, rowSize, fp); }
写 BMP 时最容易漏掉的三个字段校验点
手动生成 BMP 文件头时,90% 的失败源于这三个字段没算准,导致系统拒绝打开或显示为黑图。
立即学习“C++免费学习笔记(深入)”;
-
bfSize必须等于整个文件字节数:即sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + pixelDataSize,少 1 字节都不行 -
biSizeImage必须等于rowSize * abs(height);设为 0 在部分旧工具中会被忽略,但新版本(如 Windows 10 Photo App)会直接报“损坏的图像” -
bfOffBits必须等于sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)(无调色板时);若有调色板(如 8 位图),还要加上调色板字节数(256 * sizeof(RGBQUAD))
这些字段之间强耦合,改一个就得重算另外两个。建议封装成函数,传入 width/height/bits,自动填全。











