考虑以下在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;
网上设置断点,并在调试器中从address开始查看内存窗口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
两个问题:
为什么sizeof(str_test)
12个装8个装?
为什么内存布局相同且不依赖于包值?
为什么pack 8的sizeof(str_test)是12?
从MSDN文档:
成员的对齐将在n的倍数或成员大小的倍数(以较小者为准)的边界上。
在您的情况下,最大成员是4个字节,小于8个,因此将使用4个字节进行对齐。
为什么内存布局相同且不依赖于包值?
不允许编译器对结构成员重新排序,但是可以填充成员。对于包装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)根据类型更改了填充位置,在这种情况下,编译器(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
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句