C/C++内存(字节)对齐

1.什么是字节对齐:

(1)对于基本数据类型,如果一个变量占用n个字节,则该变量在内存中的起始地址必须是n的整数倍,即:存放起始地址 % n = 0。比如,int型变量占用4个字节,则int a; 变量a在内存中的起始地址必须是4的整数倍。
(2)对于结构体类型,那么结构体的起始地址是其最宽数据类型成员的整数倍。比如下面的结构体:

struct Stu{
	int a;
	char b;
	double c;
}
//定义结构体变量
struct Stu st;

对于结构体变量st,其成员变量占用字节数最多的变量类型是double,占用8个字节,所以变量st在内存中的起始地址是8的整数倍。

2.为什么要内存对齐

为了提高程序的执行效率。
具体来说就是访问内存速度更快。实际内存读取时,内存单元每n个一组,一次读一组。
实质上是空间换时间。

3.字节对齐的原则:

这涉及到了有没有杂注: #pragma pack(n)的问题,可以使用#pragma pack(n)来添加杂注,改变结构体成员的对齐方式。VS中默认杂注为8,可以使用

#pragma pack(show)

来查看默认的杂注,在程序中添加上面的这行代码,然后生成解决方案,就可以查看默认的杂注。如下:

warning C4810: 杂注 pack(show) 的值 == 8

当然,也可以手动设置,设置方式为:

#pragma pack(n)

其中n用来设定变量以n字节对齐方式,可以设定的值包括:1、2、4、8,VS中默认是8
若需要取消强制对齐方式,可用命令

#pragma pack()

不过需要注意的是:#pragma pack(n)无法影响结构体开始位置,只能影响结构体中成员的对齐方式

字节对齐具体原则

  • 1.从偏移为0的位置开始存储;

  • 2.如果没有定义杂注#pragma pack(n)

    • 2.1 sizeof的最终结果必然是结构体内部最大成员的整数倍,不够补齐;
    • 2.2 结构体内部各个成员的首地址必然是自身大小的整数倍;
  • 3.如果定义了#pragma pack(n)

    • 3.1 sizeof的最终结果必然是min(n,结构体内部最大成员)的整数倍,不够补齐;

    • 3.2 结构体内部各个成员的首地址必然是min(n,自身大小)的整数倍。

4.实例

下面结合实例分析,来了解字节对齐。
说明:我使用win10,VS编译器。int占4个字节,char占1个字节,double占8个字节
首先,不使用杂注的情况。

不使用杂注

#include

//sizeof(S1)=24,这个是容易理解的
struct S1{		//没有杂注
	int i;		//0-3
	char j;		//4-7
	int a;		//8-15
	double b;	//16-23
};

//sizeof(S2)=24,如果按照单个成员变量来分析,结构体占用是20个字节,
//但是因为结构体的起始地址是最宽成员类型的整数倍,即是8的整数倍,所以sizeof(S2)=24
struct S2{		//没有杂注
	int i;		//0-3
	char j;		//4-7
	double b;	//8-15
	int a;		//16-19
};

int main() {
	struct S1 s1;
	struct S2 s2;
	printf("%d\n", sizeof(s1));
	printf("%d\n", sizeof(s2));

	return 0;
}

分析:
(1)对于结构体S1,int i占用4个字节,并且存放的起始地址是4的整数倍,由于刚开始是从0开始的,自动满足,存储位置为0-3char j占用1个字节,如果没有字节对齐的话,变量j存放在位置4就行了,但是因为字节对齐,int a,也占用4个字节,且存放的起始位置是4的整数倍,所以变量a必须从位置8开始存放。所以变量j占用位置为4-7。同理,int a存放位置为8-15double b存放的位置为16-23。此时整个结构体的大小是24,正好是8的整数倍,符合结构体的对齐原则,所以sizeof(S1)=24
(2)对于结构体S2,参照结构体S1的分析,int i存放在0-3char j存放在4-7double b存放在8-15int a存放在16-19。此时结构体大小为20个字节,但是由于结构体对齐原则(结构体的起始地址是最宽成员类型的整数倍),也就是起始位置必须是8的整数倍,所以sizeof(S2)=24,而不是等于20。
执行结果:
在这里插入图片描述

#pragma pack(1)的情况

代码:

#include

struct S1{		//有杂注,n=1
	int i;		//0-3
	char j;		//4
	int a;		//5-8
	double b;	//9-16
};

struct S2{		//有杂注,n=1
	int i;		//0-3
	char j;		//4
	double b;	//5-12
	int a;		//13-16
};

int main() {
	struct S1 s1;
	struct S2 s2;
	printf("%d\n", sizeof(s1));
	printf("%d\n", sizeof(s2));

	return 0;
}

分析:
(1)对于结构体S1,int i占用4个字节,并且存放的起始地址是4的整数倍,由于刚开始是从0开始的,自动满足,存储位置为0-3char j占用1个字节,由字节对齐具体原则3.2char j的首地址是min(1,1)的整数倍,也就是1的整数倍,所以存放位置为4。同理int a的首地址也是1的整数倍,存放位置为5-8double b的首地址也是1的整数倍,存放位置为9-16。此时结构体占用的空间是17,由字节对齐具体原则3.1,结构体的占用空间必须是min(1,8)的整数倍,也就是1的整数倍,所以17是符合的。
(2)对于结构体S2,参照结构体S1的分析。
结果:
在这里插入图片描述

#pragma pack(2)的情况

代码:

#include

struct S1{		//有杂注,n=2
	int i;		//0-3
	char j;		//4-5
	int a;		//6-9
	double b;	//10-17
};

struct S2{		//有杂注,n=2
	int i;		//0-3
	char j;		//4-5
	double b;	//6-13
	int a;		//14-17
};

int main() {
	struct S1 s1;
	struct S2 s2;
	printf("%d\n", sizeof(s1));
	printf("%d\n", sizeof(s2));

	return 0;
}

分析:
(1)对于结构体S1,int i占用4个字节,并且存放的起始地址是4的整数倍,由于刚开始是从0开始的,自动满足,存储位置为0-3char j占用1个字节,由字节对齐具体原则3.2char j的首地址是min(1,2)的整数倍,也就是1的整数倍,所以应该存放位置为4。但是int a的首地址是min(4,2)的整数倍,也就是2的整数倍,所以char j的存放位置为4-5int a的存放位置为6-9double b的存放位置为10-17。此时结构体的大小为18,也符合字节对齐具体原则3.1。所以sizeof(S1)=18
(2)对于结构体S2,参照结构体S1的分析。
结果:
在这里插入图片描述

你可能感兴趣的:(C++,c++,c语言,开发语言)