结构对齐填充、最大填充大小和结构成员的顺序

Posted

技术标签:

【中文标题】结构对齐填充、最大填充大小和结构成员的顺序【英文标题】:Structure alignment padding, largest size of padding, and order of struct members 【发布时间】:2016-09-18 12:58:49 【问题描述】:

自从我发现我的 sizeof() 运算符没有返回我预期的结果以来,我一直在学习结构数据填充。根据我观察到的模式,它将结构成员与最大的数据类型对齐。比如……

struct MyStruct1

    char a;     // 1 byte
    char b;     // 1 byte
    char c;     // 1 byte
    char d;     // 1 byte
    char e;     // 1 byte
                // Total 5 Bytes

    //Total size of struct = 5 (no padding)
;

struct MyStruct2

    char a;     // 1 byte
    char b;     // 1 byte
    char c;     // 1 byte
    char d;     // 1 byte
    char e;     // 1 byte
    short f;    // 2 bytes
                // Total 7 Bytes

    //Total size of struct = 8 (1 byte of padding between char e and short f
;

struct MyStruct3

    char a;         // 1 byte
    char b;         // 1 byte
    char c;         // 1 byte
    char d;         // 1 byte
    char e;         // 1 byte
    int f;          // 4 bytes
                    // Total 9 bytes

    //Total size of struct = 12 (3 bytes of padding between char e and int f
;

但是如果让最后一个成员为 8 字节数据类型,例如 long long,它仍然只添加 3 个字节的填充,形成一个 4 字节对齐的结构。但是,如果我在 64 位模式下构建,它实际上会对齐 8 个字节(最大的数据类型)。我的第一个问题是,我说它使成员与最大的数据类型对齐是错误的吗?此语句对于 64 位构建似乎是正确的,但在 32 位构建中仅适用于最多 4 字节的数据类型。这与 CPU 的本机“字”大小有关吗?还是程序本身?

我的第二个问题是,以下是否会完全浪费空间和糟糕的编程?

struct MyBadStruct

    char a;             // 1 byte
    unsigned int b;     // 4 bytes
    UINT8 c;            // 1 byte
    long d;             // 4 bytes
    UCHAR e;            // 1 byte
    char* f;            // 4 bytes 
    char g;             // 1 byte
                        // Total of 16 bytes

    //Total size of struct = 28 bytes (12 bytes of padding, wasted)
;

谢谢。

【问题讨论】:

它根据可能与大小不同的对齐方式对齐。 char[42] 的对齐方式为 1,即使它的大小为 42。您可以直接询问编译器数据类型与 alignof 的对齐方式是什么。 doubles 发生了一件有趣的事情,否则它很简单。 有关发生的详细信息,请记住这取决于编译器和平台。发生这种情况的原因,请参阅***.com/questions/381244/purpose-of-memory-alignment。 【参考方案1】:

填充是如何完成的,不是标准的一部分。所以它可以在不同的系统和编译器上以不同的方式完成。通常这样做是为了使变量以那里的大小对齐,即 size=1 -> no alignment, size=2 -> 2 byte alignment, size=4 -> 4 byte alignment 等等。对于 size=8,通常是 4 或 8 个字节对齐。它本身的结构通常是 4 或 8 个字节对齐的。但是 - 只是重复一遍 - 它依赖于系统/编译器。

在您的情况下,它似乎遵循上述模式。

所以

char a;
int  b;

将 3 个字节填充到 4 个字节对齐 int。

char a1;
int  b1;
char a2;
int  b2;
char a3;
int  b3;
char a4;
int  b4;

最终将是 32 字节(再次以 4 字节对齐 int)。

但是

int  b1;
int  b2;
int  b3;
int  b4;
char a1;
char a2;
char a3;
char a4;

将只有 20,因为 int 已经对齐。

因此,如果内存很重要,请将最大的成员放在首位。

但是,如果内存无关紧要(例如,因为结构体的使用不多),最好将事物保持在逻辑顺序中,以便人类易于阅读代码。

【讨论】:

【参考方案2】:

以下所有内容都取决于实现。不要依赖它来确保程序的正确性(但一定要利用它来调试或提高性能)。

一般来说,每种数据类型都有一个首选对齐方式。这永远不会大于类型的大小,但可以更小。

在 32 位模式下编译时,您的编译器似乎在 32 位边界上对齐 64 位整数,但在 64 位模式下在 64 位边界上对齐。

关于MyBadStruct的问题:一般来说,你的代码写得简单易懂;仅当您(通过测量)知道您有问题时才做任何其他事情。话虽如此,如果您按大小(最大在前)对成员变量进行排序,您将最小化填充空间。

【讨论】:

【参考方案3】:

通常,减少编译器插入的填充量的最佳方法是将结构内的数据成员从最大到最小排序:

struct MyNotSOBadStruct

    long d;             // 4 bytes
    char* f;            // 4 bytes
    unsigned int b;     // 4 bytes
    char a;             // 1 byte
    UINT8 c;            // 1 byte
    UCHAR e;            // 1 byte
    char g;             // 1 byte
                        // Total of 16 bytes


;

大小可能因 32 位和 64 位操作系统而异,因为指针的大小会发生变化

现场版:http://coliru.stacked-crooked.com/a/aee33c64192f2fe0

我得到大小 = 24

【讨论】:

以上是关于结构对齐填充、最大填充大小和结构成员的顺序的主要内容,如果未能解决你的问题,请参考以下文章

结构体变量字节填充

结构体变量的sizeof计算

Python 和 C 结构之间的大小不匹配,默认结构对齐/填充

内存对齐:C/C++编程中的重要性和技巧

c++数据对齐/成员顺序&继承

结构体大小的计算,对齐