结构体的内存对齐

       首先,先看一段代码:(windows下32位)

struct s1
{
	int a;
	char b;
	double c;
};
struct s2
{
	char a;
	double b;
	int c;

};

我们看看这两个结构体大小为多少?


我们可以发现这两个结构体的大小不仅不是13,而且还不相同!

造成这种结果的原因就是接下来我们要探讨的——内存对齐。

内存对齐的原因:

①平台原因(移植原因):各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取,否则会抛出异常。

②性能原因:经过内存对齐后,cpu的访问速度会得到大大的提升。

原因在于,大部分的处理器都会以双字节、4字节、8字节等作为单位来存取内存(在cpu眼中,内存都是一块一块的),假设处理器以4字节的存取粒度处理,则该处理器只能从地址为4的倍数的内存开始读取数据,如果没有内存对齐,当cpu处理一个以地址1开始的int类型的变量时,先访问地址0—3处内存,在访问4—7的,需要访问两次内存才能读取数据,如果内存对齐,则处理器只需访问一次内存就可以读取数据,效率提高了很多(理论上对任何类型的变量访问可以从任何地址开始,但实际的计算机系统对基本类型的数据在内存中的存放位置有着限制)。

结构体内存对齐的规则:

(1)第一个成员在与结构体变量偏移量为0的地址处。
(2)其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    //对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的值为8
    linux中的默认值为4
(3)结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
(4)如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

#pragma pack(n)

不同平台下的编译器的pragma pack的默认值不同,可以通过n来修改(n为1、2、4、8、16)

现在再来开开始的两个例子:(在win32下默认有效对齐值为8)

struct s1
{
	int a;
	char b;
	double c;
};

第一个成员为int a,起始偏移为0,占了4个字节;第二个成员char b,偏移量为(成员大小1<默认对齐值8)1的倍数,起始偏移为4,是1的倍数,符合;第三个成员double c,偏移量为(成员大小8=默认对齐值8)8的倍数,起始偏移为5不符合对齐规则,当起始地址偏移为8时刚好符合,因此需要在起始偏移为5处后补三个字节,此时,共有16个字节,是最大对齐数8的倍数,符合,因此,该结构体的大小为16。

struct s2
{
	char a;
	double b;
	int c;
};

第一个成员char a,起始偏移为0,占了1个字节;第二个成员double b,偏移量为(成员大小8=默认对齐值8)8的倍数,所以b的起始偏移必须在8处,在成员a后补了7个字节,占了8个字节,;第三个成员int c,偏移量为(成员大小4<默认对齐值8)4的倍数,所以c的起始偏移在16处,占了4个字节;此时共占了20个字节,又因为结构体大小为最大对齐数8的倍数,所以,向后补充4个字节,共24个字节,刚好是8的倍数,因此,该结构体大小为24。

再来看一个简单的结构体嵌套的例子:

struct s3
{
	int a;
	struct s2 B;
	char c;
};

这个结构体的大小为40;

第一个成员int a,起始偏移为0,占了4个字节;第二个成员是一个结构体s2,该结构体的最大对齐数为其成员中的最大对齐数8,结构体大小为24,因此,起始偏移在8处,占了24个字节;最后一个成员char c,起始偏移为32,占了一个字节;结构体总大小为最大对齐数8的整数倍,因此,大小为40。




长度长度

猜你喜欢

转载自blog.csdn.net/l__xing/article/details/79541654