C语言进阶-结构体内存对齐和修改默认对齐参数

1. 结构体对齐规则

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

2. 代码举例

struct S1
{
	char c1;
	int i;
	char c2;
};

struct S2
{
	char c1;
	char c2;
	int i;
};

int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

运行结果:
12 
8

3. 代码分析

在这里插入图片描述

首先,根据规则1,c1放在偏移量为0的位置上,i其次,根据规则2,本身得大小是四个字节,VS默认8个字节取较小值为4,所以i要放在偏移量为4的倍数,所以i放在4这个位置上,中间大的三个字节虽然分配了,但未被使用,同理c2放在8这个位置上,最后,根据规则3,c1的对齐数为1,i的对齐数位4,c2的对齐数为4,c2对齐数为1,所以结构体最大对齐数为4的整数倍,所以当加到11这个位置后,刚好是4的整数倍,所以结构体大小为12个字节.

这里我们可以通过offsetof函数来求偏移量

#include <stdio.h>
#include <stddef.h>

struct S1
{
	char c1;
	int i;
	char c2;
};

struct S2
{
	char c1;
	char c2;
	int i;
};

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

	printf("%d\n", offsetof(struct S1 ,c1));
	printf("%d\n", offsetof(struct S1 ,i));
	printf("%d\n", offsetof(struct S1 ,c2));

	return 0;
}
运行结果:
12
8
0 
4
8

c2分析图如下

在这里插入图片描述

所以结构体c2占8个字节

4. 练习

4.1 分析结构体S3

struct S3
{
	double d;//0~7
	char c;//8
	int i;//12~15
};

分析:
首先,d从偏移量为0到7的位置占用8个字节,所以d从0到7,之后c在8位置处,
因为i为int地址要求4的倍数,所以i从12开始到15结束,
所以结构体大小为16个字节。

4.2 分析结构体S4

struct S4
{
	char c1;//0
	struct S3 s3;//8~23
	double d;//24~31
};

首先,c1在偏移量为0的位置,根据规则4,s3最大对齐数为8的整数倍的地址,
所以s3从8开始到23,d自身大小是8,默认对齐数也是8,
所以d从偏移量为24位置处到31,所以结构体大小为32个字节

5. 为什么要使用内存对齐

5.1 平台原因(移植原因)😗

结构体的声明不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

5.2 性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总体来说

结构体的内存对齐是拿空间来换取时间的做法。

6. 默认修改对齐数

6.1 案例分析1:

struct S
{
	int i;//0~3
	//4~7
	double d;//8~15
};

按照原来分析,如果我们不修改默认偏移量那么结构体大小为16个字节。

#pragma pack(4)
struct S
{
	int i;//0~3
	double d;//4~11
};
#pragma pack()

这里#pragma pack(4)的意思是吧默认对齐数改为4,
最后的#pragma pack()意思是改回原来默认对齐数。

首先i从0偏移量开始,d的大小是8,默认对齐数改为4,取其较小值为4,
所以d从偏移量为4的位置处到11,所以结构体大小为12个字节。

6.2 案例分析2:

#pragma pack(1)
struct S1
{
	char c1;//0
	int i;//1~5
	char c2;//6
};
#pragma pack()

因为默认对齐数修改为1,任何数都是1的倍数,所以不存在对,
结构体大小就是成员类型大小之和,所以这里结构体大小为6。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星光终将不负赶路人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值