C语言学习笔记——内存对齐

一、什么是内存对齐

  • 我们先来讨论一个问题,结构体的大小
struct s1
{ 
	char c1;
	int i;
	char c2;
};
  • 这个结构体的大小是多少呢?我相信你内心一定有一个答案,6字节;按照常理来说两个char型,一个int型,大小就是6字节。但正确答案是12字节。
  • 再来看看下面这个结构体的大小
struct s1
{
	char c1;
	char c2;
	int i;
};
  • 有的人心理犯嘀咕,这不是一样吗?只是调换了定义顺序,还能影响大小?当然会!这样定义的话,结构体大小是8字节。
  • 而这就是内存对齐造成的结果。 在我们学习计算机的时候,可能想当然的认为cpu可以访问内存的任意地方,但事实上cpu只能从内存的特定地方访问有限的数据。
    在这里插入图片描述

正常情况下应该是这样存储的,但是我们说过,cpu只能从特定的地方访存。现在我们假设cpu只能从4的倍数处访问内存。访问char c1的时候从0(这里的0指的是相对于结构体起始地址的偏移量,后续0地址均指偏移地址)地址处访问;访问int i的时候它只能先访问四个字节然后舍弃第一个字节,在访问第最后一个字节,然后把两个片段拼接起来。这样的话cpu就要访问两次内存,导致效率低下。
在这里插入图片描述

而如果我们这样存储,舍弃掉中间三个字节,cpu在访问int i时就可以一次访问完成。

  • 总的来说,内存对齐就是舍弃空间赢的时间的做法。

二、如何内存对齐

  • 第一个成员在与结构体变量起始地址偏移量为0的地址处
  • 其他成员要对齐到某个数字(对齐数)的整数倍的地址处。
  • 对齐数 = 编译器默认对齐数与该成员大小的较小值(vs默认8,Linux默认4);如果是聚合数据类型(数组和结构体),对齐数为该聚合类型中成员的最大对齐数。
  • 结构体总大小为最大对齐数(每个成员都有一个对齐数)的整数倍。

现在我们再来看一下这个例子

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

在这里插入图片描述
在内存中的实际储存情况就是这样子的。这样的话占了9个字节,但是结构体的大小要能整除成员中最大的对齐数。s1中最大的对齐数是4,从9往后可以整除4的就是12了,所以s1结构体的大小为12字节。

再看一下这个例子

struct s1
{
	char c1;
	int a[3];
	char c2;
};
struct s2
{
	char c1;
	struct s1 A;
	char c2;
};

这里s1中,c1在0地址处存储,a[3]对齐数为4,从4号地址开始存,占12个字节。c2对齐数为1,紧挨着a[3],存在16号地址,总共占17个字节,但是结构体大小要能整除自身最大对齐数,所以最终大小为20个字节。
s2中c1还是存在0地址处,第二个元素是结构体,根据定义对齐数为自身成员最大对齐数4,从4号地址开始存,占20个字节。c2存在24号地址,总共占25个字节,保证整除自身最大对齐数,最终大小为28个字节。

三、修改默认对齐数

#pragma pack(4)
struct s1
{
	char c1;
	int a[3];
	char c2;
};
#pragma pack()
struct s2
{
	char c1;
	struct s1 A;
	char c2;
};

#pragma pack()这条语句就是修改默认对齐数的,这里我们先将默认对齐数修改为4,当s1编译后在恢复默认,所以在存储时,s1的默认对齐数是4,s2的默认对齐数是8(vs环境下)。

C语言中,除了结构体,还有联合也要注意内存对齐。
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值