如何将 Visual Studio 中的结构打包为包含 uint32_t 的 24 位?

How to pack a struct in Visual Studio to 24 bits that contains an uint32_t?

我正在尝试将现有应用程序从 32 位 ARM 微控制器移植到 Microsoft Windows 等桌面平台。 GCC 在 ARM 上使用,我能够使用 32 位 MinGW 编译器在 windows 上成功编译应用程序,但是我使用 Microsoft 的 Visual Studio 编译器没有成功,这就是我的原因在这里寻求帮助。

这是我的应用程序正在执行的操作:

我有一些帧缓冲区,每个像素由三个字节组成,所以我的内存看起来像 RGBRGBRGB 等等。 我在 ARM 上使用 DMA 通道将像素推送到显示器,我的显示器直接理解这种内存布局。

我还想节省一些 CPU 周期,所以我想使用 ARM 的饱和 ADD __UQADD8 来绘制我的帧缓冲区,使用单个操作在所有三个通道上执行饱和添加。

为此,我需要所有三个通道都在一个整数中可用,以用作 __UQADD8 的参数。

这就是为什么我对我的帧缓冲区的一个像素使用联合来提供对单独通道的访问,方法是提供一个包含每个 R、G、B 的结构作为 uint8_t 并提供与一个相同的内存24 位宽整数标记数据:

union Rgb {
    struct {
        uint8_t r;
        uint8_t g;
        uint8_t b;
    } ch;
    unsigned int data : 24 __attribute__((__packed__));
}

数据整数加上24位的宽度和packed属性,限制整数的宽度为三个字节。然后我可以像这样使用像素中的数据:

Rgb Rgb::operator+(const Rgb & op) {
    __UQADD8(data, op.data);
    return Rgb(data);
}

请注意,__UQADD8 神奇地只写入了我的整数的四个字节中的三个,并且不会改变我的帧缓冲区中下一个 RGB 的 R 通道。

下面的测试程序证明了我的 RGB 在使用 GCC 时都是紧凑的:

#include <iostream>
#include <stdint.h>

union Rgb {
struct {
                uint8_t r;
                uint8_t g;
                uint8_t b;
        } ch;
    unsigned int data : 24 __attribute__((packed));
} ;

int main()
{
    std::cout << "Sizeof(Rgb) = "  << sizeof(Rgb) << std::endl;
    return 0;
}

要使用 MSVC 编译示例,必须删除 __attribute__packed。该程序运行,但给出 4 作为输出。 MSVC 中还有许多其他属性可以使用,包括 #pragma pack、未对齐的指针 __attribute__(aligned) 等等,但我发现没有组合可以将我的结构打包为三个字节的大小。

如何将我的应用程序移植到 Microsoft 的编译器,同时保持对 GCC 的功能和更好的兼容性?

奖励问题:使用 64 位编译器(GCC 或 MSVC)进行编译时如何保持功能?

this SO question and this question mention bit-packing being "implementation defined" and lead to this official MSVC documentation page 的答案是:

Adjacent bit fields are packed into the same 1-, 2-, or 4-byte allocation unit...

所以您似乎无法在 MSVC 中获得精确的 3 字节位域。我能想到的唯一选择是做类似的事情:

#pragma pack(push, 1)
union Rgb {
     struct {
          uint8_t r;
          uint8_t g;
          uint8_t b;
     } ch;
    unsigned char data[3];
};
#pragma pack(pop)

这将为您提供所需的 3 字节联合大小,但可能与使用 __UQADD8() 不兼容,至少直接不兼容。