结构大小和内存布局取决于#pragma pack

Structure size and memory layout depending on #pragma pack

本文关键字:取决于 #pragma pack 布局 内存 结构      更新时间:2023-10-16

考虑下面用vc++ 2010编译的程序:

#pragma pack(push, 1)    // 1, 2, 4, 8
struct str_test
{
    unsigned int n;
    unsigned short s;
    unsigned char b[4];
};
#pragma pack(pop)
int main()
{
    str_test str;
    str.n = 0x01020304;
    str.s = 0xa1a2;
    str.b[0] = 0xf0;
    str.b[1] = 0xf1;
    str.b[2] = 0xf2;
    str.b[3] = 0xf3;
    unsigned char* p = (unsigned char*)&str;
    std::cout << sizeof(str_test) << std::endl;
    return 0;
}

我在return 0;行上设置断点,并在调试器中查看内存窗口,从地址p开始。我得到以下结果(sizeof和内存布局,取决于pack):

// 1 - 10    (pack, sizeof)
// 04 03 02 01 a2 a1 f0 f1 f2 f3
// 2 - 10
// 04 03 02 01 a2 a1 f0 f1 f2 f3
// 4 - 12
// 04 03 02 01 a2 a1 f0 f1 f2 f3
// 8 - 12
// 04 03 02 01 a2 a1 f0 f1 f2 f3

两个问题:

  1. 为什么sizeof(str_test)是12包8?

  2. 为什么内存布局是相同的,不依赖于包值?

为什么第8包的sizeof(str_test)是12 ?

来自MSDN文档:

成员的对齐将位于an的倍数或成员大小的倍数,以二者为准小。

在你的例子中,最大的成员是4字节,小于8,所以4字节将用于对齐。

为什么内存布局是相同的,不依赖于包的值?

编译器不允许对结构体成员重新排序,但可以填充成员。在pack 8的情况下,它执行以下操作:

#pragma pack(push, 8)    // largest element is 4bytes so it will be used instead of 8
struct str_test
{
    unsigned int n; // 4 bytes
    unsigned short s; // 2 bytes        
    unsigned char b[4]; // 4 bytes
    //2 bytes padding here;
};
#pragma pack(pop)

因此sizeof(str_test)将为12

嗯,似乎编译器(MSVC2010)根据类型改变填充位置,在unsigned char b[4];的情况下,它在结构的末尾放置了两个字节填充。在您的示例中,2字节cc cc恰好位于字符数组之后。

#pragma pack(push, 8)    // largest element is 4bytes so it will be used instead of 8
struct str_test
{
    unsigned int n; // 4 bytes
    unsigned short s; // 2 bytes 
    //2 bytes padding here;       
    int;  // 4 bytes
};
#pragma pack(pop)

我所做的是将最后一个成员从char[4]更改为int,并且可以通过在两种情况下分别减去最后和第一个成员的地址6和8来验证。

最后一个成员int的内存转储如下

04 03 02 01 a2 a1 cc cc f0 f1 f2 f3

最后一个成员unsigned char[4]的内存转储如下

04 03 02 01 a2 a1 f0 f1 f2 f3 cc cc