拷贝BMP文件头时校验失败,结构体数据读取异常求助
问题排查:BMP文件头读取异常导致格式校验失败
问题背景
尝试将BMP文件头拷贝至另一个文件,同时校验文件是否为24位无压缩格式,但执行时触发格式错误提示,打印结构体数据发现数值异常。
结构体定义代码
typedef uint8_t BYTE; typedef uint16_t WORD; typedef uint32_t DWORD; typedef int32_t LONG; // Hold Bitmap File Header Information typedef struct { WORD bfType; // File Type MUST BE BM DWORD bfSize; // Bitmap File: Size in Bytes WORD bfReserved1; // Reserved: MUST BE 0 WORD bfReserved2; // Reserved: MUST BE 0 DWORD bfOffBits; // Offset: Beginning of BITMAPFILEHEADER to Bitmap Bits } BITMAPFILEHEADER; // Hold Bitmap Info Header Information typedef struct tagBITMAPINFOHEADER { DWORD biSize; // Bytes required by the structure LONG biWidth; // Specific the width of the bitmap in pixels LONG biHeight; // Height of bitmap in pixels WORD biPlanes; // Set number of planes to target device. HAS to be set to 1 WORD biBitCount; // Number of bits per pixel || Uncompressed Formats Average Number Of Bits Per Pixel DWORD biCompression; // Type of compression DWORD biSizeImage; // Size of image in bytes LONG biXPelsPerMeter; // Horizitonal resolution in pixels per meter LONG biYPelsPerMeter; // Vertical resolution in pixels per meter DWORD biClrUsed; // Specifies no. of colour indices in the color table DWORD biClrImportant; // Speifiies no. of colours that are important for displaying bitmap } BITMAPINFOHEADER, * LPBITMAPINFOHEADER, * PBITMAPINFOHEADER;
读取校验代码
FILE* Original = fopen(argv[1], "rb"); if (Original == NULL) { printf("ERROR: Couldn't open file\n"); printf("Check File Name\n\n"); } // Read Header BITMAPFILEHEADER BF; fread(&BF, sizeof(BITMAPFILEHEADER), 1, Original); // Read File Header BITMAPINFOHEADER BI; fread(&BI, sizeof(BITMAPINFOHEADER), 1, Original); // Read Info Header // Check File -> 24-Bit Uncompressed if (BF.bfType != 0x4D42 || BF.bfOffBits != 54 || BI.biSize != 40 || BI.biBitCount != 24 || BI.biCompression != 0) { printf("ERROR: Incorrect File Type\n"); printf("Check Bitmap Header!\n\n"); fclose(Original); }
异常输出
bfType: 19778
bfOffBits: 8126464
biSize: 83886080
biBitCount: 0
biCompression: -83886080
问题原因及解决方法
1. 结构体字节对齐问题(核心原因)
编译器默认会对结构体成员进行字节对齐(通常是4或8字节),导致结构体实际大小大于BMP标准头的大小:
- 标准
BITMAPFILEHEADER为14字节,BITMAPINFOHEADER为40字节 - 对齐后结构体可能被填充额外字节,
fread读取时会多读入无效数据,导致后续字段错位
解决方法:强制结构体按1字节对齐,避免编译器填充:
- MSVC编译器:在结构体定义前后添加对齐指令
#pragma pack(push, 1) // 结构体定义放在这里 #pragma pack(pop)
- GCC/Clang编译器:在结构体末尾添加
__attribute__((packed))
typedef struct { // 成员定义 } __attribute__((packed)) BITMAPFILEHEADER;
2. 未校验文件读取结果
当前代码未检查fread的返回值,若文件读取不完整(如文件损坏、路径错误),会导致结构体数据异常。
解决方法:每次调用fread后校验返回值,确保读取到完整的结构体:
if (fread(&BF, sizeof(BITMAPFILEHEADER), 1, Original) != 1) { printf("ERROR: Failed to read file header\n"); fclose(Original); return 1; } if (fread(&BI, sizeof(BITMAPINFOHEADER), 1, Original) != 1) { printf("ERROR: Failed to read info header\n"); fclose(Original); return 1; }
3. 跨平台字节序兼容问题
BMP文件采用小端字节序存储多字节字段,若运行在大端架构机器上,直接读取会导致数值反转。
解决方法:使用字节序转换函数将文件字节序转换为主机字节序:
#include <arpa/inet.h> // 包含ntohs/ntohl函数 // 转换BITMAPFILEHEADER字段 BF.bfType = ntohs(BF.bfType); BF.bfSize = ntohl(BF.bfSize); BF.bfOffBits = ntohl(BF.bfOffBits); // 转换BITMAPINFOHEADER字段 BI.biSize = ntohl(BI.biSize); BI.biBitCount = ntohs(BI.biBitCount); BI.biCompression = ntohl(BI.biCompression); // 其他多字节字段同理转换
内容的提问来源于stack exchange,提问作者TedSnow
相关产品推荐
相关产品推荐

