结构体内存分配问题

引入

最近上课的时候老师问我们下面这段代码:

struct test {
	char a=1;
	short l=2;
};

中a和l在内存中占几个字节,它们的排列方式是连续在一起的还是分开的? 占多少字节如果是内存对齐的话会是4字节、设置#pragma pack(1)的话则是3字节;但是他们的排列方式还真的不清楚,所以今天研究一下。

解答

首先我们先回答问题,再介绍一下原理,先写个程序看一下:

struct test {
	char a=1;
	short l=2;
};
int main()
{
	test A;
	auto address_a = &(A.a);
	auto address_l = &(A.l);
}

查看一下地址,进而查阅内存:
02.PNG
01.PNG
内存分布为0x0018FC5C-0x0018FC5F:01 cc 02 00
所以是间隔分布的,但如果我们加上#pragma pack(1):

#pragma pack(1)
struct test {
	char a=1;
	short l=2;
};
#pragma pack()

int main()
{
	test A;
	auto address_a = &(A.a);
	auto address_l = &(A.l);
}

则内存分布为:
03.PNG
变成了01 02 00 cc,符合我之前的结论。

原理

结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节;

char a;

short i;

如果a的地址是0x0000,那么i的地址将会是0x0002或者是0x0004。那么就出现这样一个问题:0x0001这个地址没有被使用;它确实没被使用。因为CPU每次都是从以2字节(16位CPU)或是4字节(32位CPU)的整数倍的内存地址中读进数据的。如果变量i的地址是0x0001的话,那么CPU就需要先从0x0000中读取一个short,取它的高8位放入i的低8位,然后再从0x0002中读取下一个short,取它的低8位放入i的高8位中,这样的话,为了获得i的值,CPU需要进行了两次读操作。
但是如果i的地址为0x0002,那么CPU只需一次读操作就可以获得i的值了。所以编译器为了优化代码,往往会根据变量的大小,将其指定到合适的位置,即称为内存对齐(对变量i做内存对齐,a、i之间的内存被浪费,a并未多占内存)。
结构体所占用的内存与其成员在结构体中的声明顺序有关,其成员的内存对齐规则如下:
(1)每个成员分别按自己的对齐字节数和PPB(指定的对齐字节数,32位机默认为4)两个字节数最小的那个对齐,这样可以最小化长度。
(2)复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。
(3)结构体对齐后的长度必须是成员中最大的对齐参数(PPB)的整数倍,这样在处理数组时可以保证每一项都边界对齐。
(4)计算结构体的内存大小时,应该列出每个成员的偏移地址,则其长度=最后一个成员的偏移地址+最后一个成员数的长度+最后一个成员的调整参数(考虑PPB)。
注意:
(1)字节对齐取决于编译器;
(2)一定要注意PPB大小,PPB大小由pragam pack(n)指定;
(3)结构体占用的字节数要能被PPB整除。

posted @ 2018-09-05 01:23  MrYun  阅读(245)  评论(0编辑  收藏  举报