8

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 吗?

任何澄清表示赞赏!

4

3 回答 3

8

您的摘录解释了这一点,“以较小者为准”。在 32 位平台上,anint是 4 个字节。4 小于 8。所以它有 4 字节对齐。

packpragma 导致事物被打包,而不是被解包。除非有理由,否则它不会填充。

于 2012-04-21T10:26:04.577 回答
5

请记住,为什么首先对齐很重要。它可以让 CPU 快速读取内存,而无需多路复用字节。cpu 从不一次读取一个结构,它只访问它的成员。所以 Foo 结构是 12 个字节的事实并不重要。只有其成员的对齐很重要。鉴于没有 Foo 成员的对齐要求大于 4,Bar.foo 成员只需要对齐到 4。

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

于 2012-04-21T10:58:22.240 回答
2

正如您的报价所说 - 要测试 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 %d\n",
           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
于 2012-04-21T10:29:14.103 回答