【问题标题】:How works the sizeof function on a struct with and without bit field? (padding)sizeof 函数如何在有和没有位域的结构上工作? (填充)
【发布时间】:2024-05-02 11:55:01
【问题描述】:

我在 C++ 中使用位字段测试结构的行为,但遇到了一些令人困惑的问题。我的操作系统是 Windows 10 x64。

我使用的代码如下:

struct BitFieldTest
{
    bool flag1 : 1;
    bool flag2 : 1;
    bool flag3 : 1;
    bool flag4 : 1;
    unsigned char counter1 : 4;
    unsigned int counter2 : 4;
};

struct NormalFieldTest
{
    bool flag1;
    bool flag2;
    bool flag3;
    bool flag4;
    unsigned char counter1;
    unsigned int counter2;
};
struct NormalFieldTest2
{
    bool flag1;
    bool flag2;
    bool flag3;
    bool flag4;
    unsigned char counter1;
};

int main()
{
    std::cout << "Size of bool: " << sizeof(bool) << std::endl;
    std::cout << "Size of unsigned char: " << sizeof(unsigned char) << std::endl;
    std::cout << "Size of unsigned int: " << sizeof(unsigned int) << std::endl;

    std::cout << "Size of struct with bit field: " << sizeof(BitFieldTest) << std::endl;
    std::cout << "Size of struct without bit field: " << sizeof(NormalFieldTest) << std::endl;
    std::cout << "Size of struct without bit field: " << sizeof(NormalFieldTest2) << std::endl;
    return 0;
}

输出是:

Size of bool: 1
Size of unsigned char: 1
Size of unsigned int: 4
Size of struct with bit field: 8
Size of struct without bit field: 12
Size of struct without bit field 2: 5

我不明白为什么结构的大小是这样的。任何人都可以解释或分享有关该主题的一些链接吗?

【问题讨论】:

标签: c++ alignment sizeof bit-fields


【解决方案1】:

只要位域的类型相同,位域只能压缩在一起。所以一个位字段:

struct Test
{
    char Test1 : 1;
    char Test2 : 1;
    char Test3 : 1;
    char Test4 : 1;
    short Test5 : 4;
}

不会是一个字节长,而是四个。

这是因为两件事 - 首先,位字段不跨越类型边界。 char 中的位域不能与 short 中的位域混用。

其次,为了对齐的目的,short 必须放在结构开头的两个字节边界上。因此编译器将其更改为:

struct Test
{
    char Test1 : 1;
    char Test2 : 1;
    char Test3 : 1;
    char Test4 : 1;
    char BitPadding : 4;
    char AlignmentPadding;
    short Test5 : 4;
    short BitPadding2 : 12;
}

这使结构体长达四个字节。

现在我们依次遍历每个结构。

struct BitFieldTest
{
    bool flag1 : 1;
    bool flag2 : 1;
    bool flag3 : 1;
    bool flag4 : 1;
    unsigned char counter1 : 4;
    unsigned int counter2 : 4;
};

这里,flag1flag2flag3flag4 都具有相同的类型,并压缩为存储在单个字节中的四位值。 counter1 是一个不同的类型,并且有一个自然对齐,所以它移动到下一个字节边界。 counter2 也是一个不同的类型,它有四的自然对齐,所以它移动到下一个四字节边界。如果我们然后总结各个部分的大小和中间填充,我们有:

1 个字节,带有四个标志, 1 个字节,带四位计数器, 2字节对齐填充,和 4 字节,带四位计数器。

这加起来最多 8 个字节,正是编译器报告的内容。

第二个结构没有位域,但显示了对齐问题:

struct NormalFieldTest
{
    bool flag1;
    bool flag2;
    bool flag3;
    bool flag4;
    unsigned char counter1;
    unsigned int counter2;
};

在这里,我们有:

布尔标志 1 的 1 个字节, 布尔标志 2 的 1 个字节, 布尔标志 3 的 1 个字节, 布尔标志 4 的 1 个字节, 计数器 1 的 1 个字节, 3 个字节用于对齐填充,以及 计数器 2 为 4 个字节。

这加起来是 12,这也是编译器报告的。

第三个结构与第二个结构大致相同,但缺少内部对齐填充:

struct NormalFieldTest2
{
    bool flag1;
    bool flag2;
    bool flag3;
    bool flag4;
    unsigned char counter1;
};

在这里,我们有:

布尔标志 1 的 1 个字节, 布尔标志 2 的 1 个字节, 布尔标志 3 的 1 个字节, 布尔标志 4 的 1 个字节,以及 计数器 1 为 1 个字节。

正如编译器所报告的那样,这加起来最多为 5 个字节。

作为附加说明,内部类型的自然对齐也可以泄漏到结构本身中。考虑以下结构:

struct TrailingAlignment
{
    int Field1;
    short Field2;
}

这个结构看起来是 6 个字节,但会编译成 8 个字节。这是因为这个结构可能用在一个数组中,如果它的长度是 6 个字节,那么数组中的每个其他项都会包含一个未对齐的 @ 987654333@,在不支持未对齐访问的系统中造成重大问题(如某些 ARM 版本)。为避免这种情况,编译器会在结构的末尾插入两个字节,以确保数组中此结构的下一个实例将在四字节边界上对齐,假设第一个结构已正确对齐。

NormalFieldTest2 不会发生这种情况,因为 boolchar 的自然对齐是一个字节,因此无论结构在内存中的什么位置,它都会始终对齐。

【讨论】:

    最近更新 更多