c - 为什么 fread 弄乱了我的字节顺序?

标签 c struct bmp fread

我试图用 fread() 解析一个 bmp 文件,当我开始解析时,它颠倒了我的字节顺序。

typedef struct{
    short magic_number;
    int file_size;
    short reserved_bytes[2];
    int data_offset;
}BMPHeader;
    ...
BMPHeader header;
    ...

十六进制数据为42 4D 36 00 03 00 00 00 00 00 36 00 00 00; 我正在通过 fread(&header,14,1,fileIn);

将十六进制数据加载到结构中

我的问题是魔数(Magic Number)应该在哪里 0x424d//'BM' fread() 它将字节翻转为 0x4d42//'MB'

为什么 fread() 会这样做,我该如何解决它;

编辑:如果我不够具体,我需要将整个十六进制数据 block 读入结构,而不仅仅是魔数(Magic Number)。我只以魔数(Magic Number)为例。

最佳答案

这不是fread的错,但是你的 CPU,它(显然)是小端。也就是说,您的 CPU 处理 short 中的第一个字节。值为 8 位,而不是(如您所料)高 8 位。

无论何时读取二进制文件格式,都必须显式地将文件格式的字节序转换为 CPU 的 native 字节序。您可以使用以下函数来做到这一点:

/* CHAR_BIT == 8 assumed */
uint16_t le16_to_cpu(const uint8_t *buf)
{
   return ((uint16_t)buf[0]) | (((uint16_t)buf[1]) << 8);
}
uint16_t be16_to_cpu(const uint8_t *buf)
{
   return ((uint16_t)buf[1]) | (((uint16_t)buf[0]) << 8);
}

你做你的fread进入 uint8_t适当大小的缓冲区,然后您手动将所有数据字节复制到您的 BMPHeader结构,根据需要进行转换。这看起来像这样:

/* note adjustments to type definition */
typedef struct BMPHeader
{
    uint8_t magic_number[2];
    uint32_t file_size;
    uint8_t reserved[4];
    uint32_t data_offset;
} BMPHeader;

/* in general this is _not_ equal to sizeof(BMPHeader) */
#define BMP_WIRE_HDR_LEN (2 + 4 + 4 + 4)

/* returns 0=success, -1=error */
int read_bmp_header(BMPHeader *hdr, FILE *fp)
{
    uint8_t buf[BMP_WIRE_HDR_LEN];

    if (fread(buf, 1, sizeof buf, fp) != sizeof buf)
        return -1;

    hdr->magic_number[0] = buf[0];
    hdr->magic_number[1] = buf[1];

    hdr->file_size = le32_to_cpu(buf+2);

    hdr->reserved[0] = buf[6];
    hdr->reserved[1] = buf[7];
    hdr->reserved[2] = buf[8];
    hdr->reserved[3] = buf[9];

    hdr->data_offset = le32_to_cpu(buf+10);

    return 0;
}

假设 CPU 的字节顺序与文件格式的字节顺序相同,即使您现在知道它们是相同的;您无论如何都要编写转换,以便将来您的代码无需修改即可在具有相反字节顺序的 CPU 上运行。

使用固定宽度 <stdint.h> 可以让您的生活更轻松类型,通过使用无符号类型,除非绝对需要能够表示负数,并且当字符数组可以时,使用整数。我在上面的例子中做了所有这些事情。你可以看到你不需要费力地转换魔数(Magic Number),因为你唯一需要做的就是测试 magic_number[0]=='B' && magic_number[1]=='M' .

相反方向的转换,顺便说一句,看起来像这样:

void cpu_to_le16(uint8_t *buf, uint16_t val)
{
   buf[0] = (val & 0x00FF);
   buf[1] = (val & 0xFF00) >> 8;
}
void cpu_to_be16(uint8_t *buf, uint16_t val)
{
   buf[0] = (val & 0xFF00) >> 8;
   buf[1] = (val & 0x00FF);
}

32 位/64 位数量的转换留作练习。

关于c - 为什么 fread 弄乱了我的字节顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8556927/

相关文章:

c - 将BMP图像放入2D数组并在C中编辑RGB值

c - 通过 const 指针传递与通过内置类型的值传递。效率

c - 维基百科关于内存泄漏的文章中的错误

无法打印结构

c++ - 如何将字体文件集成(硬编码)到我的程序中?

c++ - 使用正确的结构将 BMP 读入内存

Java BMP 头文件

c - 通过引用错误传递结构(不完整的结构和重新声明)

c - 编译器的未定义行为

c++ - 在 C++ 中声明结构方法