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<stdio.h>

//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<stdio.h>

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<stdio.h>

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的分析。
结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中,结构体和类都是由多个成员变量组成的。为了在内存中高效地存储这些成员变量,编译器会对结构体和类进行内存字节对齐内存字节对齐是指将结构体或类中的成员变量按照一定的规则排列,使得每个成员变量的内存地址都是其长度的整数倍。这样一来,访问这些成员变量时就可以减少内存访问次数,提高访问效率。 内存字节对齐的规则如下: 1. 结构体或类的起始地址必须是其最宽基本类型成员的整数倍。 2. 结构体或类的每个成员变量相对于起始地址的偏移量必须是其类型大小的整数倍。 3. 结构体或类的总大小必须是其最宽基本类型成员大小的整数倍。 例如,一个结构体中有两个成员变量,一个是int类型,一个是char类型。如果按照默认的字节对齐规则排列,结构体的内存布局如下: ``` struct MyStruct { int a; char b; }; // 内存布局 // +---+---+ // | a | b | // +---+---+ ``` 这里,int类型占用4个字节,char类型占用1个字节。因此,编译器会按照4字节对齐的方式排列结构体。由于int类型是最宽的基本类型,所以结构体的起始地址必须是4的倍数,而char类型则放在了4字节边界上。 需要注意的是,内存字节对齐规则可能会因为编译器的不同而产生变化。有些编译器允许开发者通过预处理指令来指定结构体的对齐方式,例如: ``` struct MyStruct { int a; char b; } __attribute__((aligned(8)))); ``` 这里,`__attribute__((aligned(8)))`表示MyStruct结构体需要以8字节对齐的方式排列。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值