考虑在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
两个问题:
为什么{8}包装8的sizeof(str_test)
为12?
为什么内存布局相同且不依赖于包价值?
答案 0 :(得分:3)
为什么sizeof(str_test)对于包8来说是12?
来自MSDN docs:
成员的对齐将位于a的边界上 n的倍数或成员大小的倍数,以两者为准 小。
在你的情况下,最大的成员是4字节,小于8,所以4字节将用于对齐。
为什么内存布局相同且不依赖于包值?
编译器不会被允许重新排序struct成员,但可以填充成员。如果是第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