Win32 C/C++从字节数组读取BMP宽度和高度

Win32 C/C++ Reading BMP width and height from a byte array

本文关键字:BMP 读取 高度 数组 字节数 C++ 字节 Win32      更新时间:2023-10-16

我已经将BMP文件读取到字节数组中。因此,在研究了这个问题的答案之后:字节数组到int c++。这种从BMP信息头获取宽度和高度的方法安全正确吗?

long width, height;
memcpy(&width, &bmp[0x12], sizeof(long)); 
memcpy(&height, &bmp[0x16], sizeof(long));

这种方法会带来什么问题?

long* width = (long*)(&bmp[0x12]);
long* height= (long*)(&bmp[0x16]);

根据Wikipedia BMP文件格式,0x12是以像素为单位的位图宽度的偏移量,0x16是以像素为单位的位图高度的偏移量。

PD。我已经找到了从内存缓冲区加载位图的解决方案,但我想保持代码简单,因为我只需要位图的宽度、高度和原始数据,我也不知道这个答案是否安全。

谢谢!

这两种方法本质上都做着相同的事情,而且都有相同的基本问题:如果主机系统的字节顺序与BMP文件使用的字节顺序不同,则无法工作。这总是直接访问二进制格式中大于单个字节的值的问题。

后一种方法还有一个额外的缺点,即如果主机不能在产生的地址上进行long访问,则可能会中断。

简而言之,这两种"解决方案"都不好。最好逐字节提取值并重新构成:

static uint32_t read_uint32_t(const uint8_t *buffer, size_t offset)
{
  buffer += offset;
  const uint32_t b0 = *buffer++;
  const uint32_t b1 = *buffer++;
  const uint32_t b2 = *buffer++;
  const uint32_t b3 = *buffer++;
  return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
}

为了简洁起见,上面使用了C99的一小部分,将其移植回C89是微不足道的。

编辑:以上操作适用于任何arcihtecture,因为它不再进行直接访问。相反,它假设buffer包含little-endian格式的字节,我相信这就是x86起源的BMP格式一直使用的格式。因此,它甚至可以在大型endian机器上工作。它也不再进行可能不对齐的大型内存访问,因为所有访问都是字节大小的,应该可以工作。

只要long是32位值,memcpy就可以正常工作(我个人会使用uint32_t)。如果您知道运行代码的体系结构将始终是x86,那么强制转换将起作用。

[其他人对字节序的好观点]。

如果你关心字节顺序,那么使用类似的东西:

uint32_t width = bmp[0x12] + (bmp[0x13] << 8) + (bmp[0x14] << 16) + (bmp[0x15] << 24);

长度相同。这假设您的bmp值为unsigned char

这两种方法可能都适用于Win32。但一般来说,两者都是不安全的。第一种变体依赖于sizeof(long) == 4。第二个也依赖于此,但需要正确的数据对齐(这可能是问题,例如在ARM上)。两者都有一个共同点,即它们假设了特定的endianes,并将在例如x86和mips上显示不同的行为。