编译器将填充添加到已对齐的联合位字段成员

时间:2015-03-24 20:49:16

标签: c padding unions bit-fields

我正在尝试构建自己的库,用于在不同字节序列的整数和签名表示之间进行转换。部分原因是直接访问char缓冲区作为相同大小的整数(主要用于快速比较)。我非常担心这个项目的可移植性。我希望这个库可以在所有机器和新旧编译器上使用,而不仅仅是linux上的gcc或clang。

最初我使用整数成员的c99固定宽度整数类型。& nbsp这个工作正常。& nbsp当我从固定宽度整数切换到相同大小的位字段时问题就开始了。我这样做是为了通过不依赖于c99功能来提高可移植性。& nbsp

问题是,当我使用位字段时,gcc会抱怨插入了填充(-Werror和-Wpadded传递给gcc):
错误:将结构大小填充到对齐边界

以下是其中一个工会的例子:

旧版本,工作正常:

typedef union
{
    unsigned char bytes[4];
    uint32_t uvalue;
} upicl_uint32_be;

新版本,添加不需要的填充:

typedef union
{
    unsigned char bytes[4];
    unsigned long uvalue : 32;
} upicl_uint32_be;


当32位位字段已经对齐时,我不知道为什么会在我的工会中添加填充。

有更好的方法吗?

3 个答案:

答案 0 :(得分:1)

可能long长度为8个字节,因此位域将占用8个字节,因为它与uint32_t的大小不同。

联合类型的名称表明您希望位域基于uint32_t而不是unsigned long(或者您认为两者是相同的,这显然是不正确的)。

答案 1 :(得分:0)

您好像是在64位系统上编译代码,否则unsigned long将与uint32_t相同。您可以测试以下内容:

typedef union {
   uint32_t uvalue : 1;
} upicl_uint32_be;

...也发出警告。问题是你只有1位数据,但uint32_t强制它占用32位。

如果您不想收到警告,请使用unsigned int类型。实际上,除了一些微控制器unsigned int之外的所有系统都保证是32位。但在我看来,使用uint32_t是最好的选择,因为它始终保证是32位。

您可以编写一些#define黑客,看看哪种基本类型是32位,但我不推荐这样的黑客攻击。考虑到C99已超过15年,所以几乎所有编译器都支持它。

答案 2 :(得分:0)

假设您需要一个4字节无符号长本机,您可以使用对齐语法来设置它。

typedef union
{
    unsigned long unused : 0;
    unsigned char bytes[4];
    unsigned long uvalue : 32;

} upicl_uint32_be;

可能会成功。

另一种可能性就是颠倒顺序,以便编译器首先看到unsigned long。

typedef union
{
    unsigned long uvalue : 32;
    unsigned char bytes[4];

} upicl_uint32_be;

尝试双打的最后一件事通常是8字节

typedef union
{
    double unused;
    unsigned char bytes[4];
    unsigned long uvalue : 32;

} upicl_uint32_be;