视觉C++中的结构对齐

Structure alignment in Visual C++

本文关键字:结构 对齐 C++ 视觉      更新时间:2023-10-16

Visual C++同时提供了一个编译器开关(/Zp)和pack杂注来影响结构成员的排列。但是,我似乎对它们的工作方式有一些误解。

根据 MSDN,对于给定的对齐值 n,

成员的对齐方式将位于边界上,该边界为 n 的倍数或成员大小的倍数,以 较小。

假设包值为 8 字节(这是默认值)。在结构中,我认为任何大小小于 8 字节的成员都将处于其自身大小的倍数的偏移量。大小为 8 字节或更大的任何成员的偏移量均为 8 字节的倍数。

现在采用以下程序:

#include <tchar.h>
#pragma pack(8)
struct Foo {
    int i1;
    int i2;
    char c;
};
struct Bar {
    char c;
    Foo foo;
};
int _tmain(int argc, _TCHAR* argv[]) {
    int fooSize = sizeof(Foo); // yields 12
    Bar bar;
    int fooOffset = ((int) &bar.foo) - ((int) &bar); // yields 4
    return 0;
}

Foo结构的大小为 12 字节。所以在 Bar 内,我希望Foo成员位于偏移量 8(8 的倍数),而实际上它在偏移量 4。为什么?

此外,Foo实际上只有 4+4+1 = 9 字节的数据。编译器会自动在末尾添加填充字节。但同样,给定 8 个字节的对齐值,它不应该填充到 8 的倍数而不是 4 吗?

任何澄清表示赞赏!

你的摘录解释了这一点,"以较小的为准"。在 32 位平台上,int为 4 个字节。4 小于 8。所以它有一个 4 字节对齐。

pack杂注会导致东西被打包,而不是拆包。除非有理由,否则它不会填充。

请记住为什么对齐首先很重要。 它允许CPU快速读取内存,而无需多路复用字节。 cpu 从不一口气读取结构,它只访问其成员。 因此,Foo结构体为12字节这一事实无关紧要。 只有其成员的一致性才重要。 鉴于没有 Foo 成员的对齐要求大于 4,Bar.foo 成员只需要对齐到 4。

Foo 是 12 字节而不是 9 字节也可以使用解释。 编译器在末尾添加 3 个字节的填充,以便 Foo 数组仍然为每个数组元素正确对齐成员。

正如您的报价所说 - 要测试 8 字节对齐方式,您需要 8 种或更多字节数据类型。下面是一些显式大小类型的示例。此外,将小元素放在末尾不会显示填充,因为它可以从结构的末尾删除。

#include <stdio.h>
int
main(int argc, char *argv[])
{
    struct S {
        __int64 a;
        __int8  b;
        __int64 c;
    };
#pragma pack(push,1)
    struct T {
        __int64 a;
        __int8  b;
        __int64 c;
    };
#pragma pack(pop)
#pragma pack(push,8)
    struct U {
        __int64 a;
        __int8  b;
        __int64 c;
    };
    struct B {
        __int8 c;
        struct U s;
    };
#pragma pack(pop)
    printf("S %d T %d U %d B %dn",
           sizeof(struct S), sizeof(struct T),
           sizeof(struct U), sizeof(struct B));
    return 0;
}

使用 VC 2010 编译此内容:

C:src>cl -nologo -W3 -Od packing.c && packing.exe
packing.c
S 24 T 17 U 24 B 32