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

Structure alignment padding, largest size of padding, and order of struct members

自从我发现我的 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字节的填充,形成一个四字节对齐的结构。但是,如果我在 64 位模式下构建,它实际上会对齐 8 个字节(最大数据类型)。我的第一个问题是,我说它将成员与最大数据类型对齐是不是错了?此语句对于 64 位构建似乎是正确的,但在 32 位构建中最多只适用于 4 字节数据类型。这与 CPU 的原始 'word' 大小有关吗?还是程序本身?

我的第二个问题是,下面的内容会完全浪费 space 和糟糕的编程吗?

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)
};

谢谢。

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

您的情况似乎遵循上述模式。

所以

char a;
int  b;

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

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

将以 32 字节结束(再次将 int 对齐到 4 字节)。

但是

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

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

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

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

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

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 位而异os,因为指针的大小会改变

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

我得到尺寸 = 24

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

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

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

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