为什么fread会搞乱我的字节顺序?

Cha*_*den 11 c struct bmp fread

我试图解析一个bmp文件,fread()当我开始解析时,它会反转我的字节顺序.

typedef struct{
    short magic_number;
    int file_size;
    short reserved_bytes[2];
    int data_offset;
}BMPHeader;
    ...
BMPHeader header;
    ...
Run Code Online (Sandbox Code Playgroud)

十六进制数据是42 4D 36 00 03 00 00 00 00 00 36 00 00 00; 我正在将十六进制数据加载到struct中fread(&header,14,1,fileIn);

我的问题是幻数应该是0x424d //'BM'fread()它会翻转字节0x4d42 // 'MB'

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

编辑:如果我不够具体,我需要将整个十六进制数据块读入结构,而不仅仅是幻数.我只选择了幻数作为例子.

zwo*_*wol 15

这不是fread你的CPU 的故障,而是(显然)是小端的.也就是说,您的CPU将short值中的第一个字节视为 8位,而不是(如您所预期的那样)高8位.

每当您读取二进制文件格式时,您必须显式地从文件格式的字节顺序转换为CPU的本机字节顺序.您可以使用以下函数执行此操作:

/* 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);
}
Run Code Online (Sandbox Code Playgroud)

您将自己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;
}
Run Code Online (Sandbox Code Playgroud)

假设CPU的字节顺序与文件格式相同,即使您知道现在它们是相同的事实; 无论如何,你要编写转换,以便将来你的代码可以在没有修改的情况下在具有相反字节序的CPU上工作.

通过使用固定宽度<stdint.h>类型,使用无符号类型,除非能够绝对需要表示负数,并且在字符数组执行时使用整数,您可以使自己更轻松.我在上面的例子中完成了所有这些事情.你可以看到你不必费心地将魔术数字转换为endian,因为你唯一需要做的就是测试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);
}
Run Code Online (Sandbox Code Playgroud)

转换32/64位数量作为练习.