You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

拷贝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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 06:55:22