C | 结构体内存对齐

C | 结构体内存对齐,第1张


啊我摔倒了..有没有人扶我起来学习....


文章目录
  • 前言
  • 一、结构体内存对齐是什么?
  • 二、认识结构体内存对齐
    • 1. 先来一个思考
    • 2. 用offsetof协助一手
    • 3. 研究对齐规则
  • 三、为什么要有结构体内存对齐?
    • 1. 存在的必要
    • 2. 修改默认对齐数
  • 四、小小的总结


前言

结构体内存对齐历来是C语言学习过程中的重点,其目的是通过牺牲空间来换取时间,但是理解起来一点也不难,那我们就,学学?


一、结构体内存对齐是什么?

这是一种结构体存储在内存里的一套规则,具体如下:

  1. 结构体的第一个成员直接对齐到相对于结构体变量起始位置为0的偏移处
  2. 从第二个成员开始,要对齐到某个【对齐数】的整数倍的偏移处
    对齐数:结构体成员自身大小和默认对齐数的较小值
    VS:8
    Linus环境默认不设对齐数(对齐数是结构体成员的自身大小)
  3. 结构体的总大小,必须是最大对齐数旳整数倍
    每个结构体成员都有一个对齐数,其中最大的对齐数就是最大对齐数
  4. 如果嵌套了结构体的情况:
    嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

好了,是不是啥都没看懂?别慌,下面开始正餐


二、认识结构体内存对齐 1. 先来一个思考
#include

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;
}
  • 路飞:以上代码的输出结果是什么?
  • 贝吉塔:
  • 路飞:那让我们运行代码看看!
  • 贝吉塔:??????????
  • 贝吉塔:这是怎么回事?!
2. 用offsetof协助一手
  • 为了让我们深入了解,我们先学一个宏:
#include 
offsetof

  • 它是用来计算结构体成员相对于起始位置的偏移量。起始位置就是结构体在内存中刚开始存储的那个字节,我们假设为0,偏移量就是相对于0偏移几个字节,比如偏移量是3。
  • 我们用它计算一下
#include 
#include 

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

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

	return 0;
}
  • 输出结果为
成员Value
c10
i4
c28
  • 那我们就来看看究竟是怎么一回事~
3. 研究对齐规则
  • 根据上述结果,struct s1的成员一共占12个字节,结合偏移量,那么它们在内存中应该是这样存放的
  • 说明其中有些空间是浪费掉的
  • 我们继续观察上图:
  1. 参考内存对齐规则1(结构体的第一个成员直接对齐到相对于结构体变量起始位置为0的偏移处)得出,c1确实在0偏移量处
  2. 参考内存对齐规则2(从第二个成员开始,要对齐到某个【对齐数】的整数倍的偏移处),由于博主使用的是VS,默认对齐数为8,而每个成员自身大小与默认对齐数进行比较,较小值就是该成员的对齐数了,因此列出下表
成员自身大小默认对齐数对齐数(较小值)
c1181
i484
c2181

因此,i的对齐数为4,所以它存储的时候不能紧接在c1的背后,只能从偏 移量为4(对齐数的整数倍)处开始存储,往后占据4个字节

  1. 然后c2就紧接在i后边存储,此时偏移量为8,因为c2的对齐数是1嘛,偏移量8不就是1的整数倍嘛
  2. 再参考内存对齐规则3(结构体的总大小,必须是最大对齐数旳整数倍)可知,要先在结构体成员中找出对齐数的老大,struct S1里老大是4,那么4就是最大对齐数,此时结构体总大小就得是4的整数倍,而此时c2存储完之后是在偏移量为8处,struct S1一共占用了9个字节,所以必须往后开辟空间,即使浪费也在所不惜

那么用struct S2练练手吧,计算出结构体的大小~

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

答案是8

趁热打铁,我们继续!

struct S3
{
	double d;
	char c;
	int i;
};

答案是16

扶我起来!我还要继续

struct S4
{
	char c4;
	struct S3 s3;
	double d4;
};
  • 这个就有点难度了,不怕,我们还有对齐规则4(嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍),看起来挺绕,其实说白了都是找最大对齐数,细心一点就很容易了

    此时偏移量8至23存储的其实就是s3,只是s3里面又存储了d、c、i

答案是32

三、为什么要有结构体内存对齐? 1. 存在的必要
  • 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  • 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

针对性能原因我们试着解释一下

假设每次访问内存4个字节,不对齐的话访问i时需要两次

  • 所以说结构体内存对齐是用空间换时间的做法
2. 修改默认对齐数
  • 假如你就是不想浪费空间,咱可以用下列代码取消默认对齐数
#pragma pack(1)
  • 其实原理就是设置括号里的数字为新的默认对齐数,设为1的话相当于没有对齐的概念,所以同样可以设成其他数字(一般是2的整数倍),但是使用后想恢复的话,再用一次
#pragma pack()
  • 此时括号里不填
四、小小的总结

其实多尝试比较的话是可以看出来,拥有同样成员的结构体,所占用的内存是不一样的

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

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

刚刚一起分析过的,struct S1大小是12,struct S2是8,通过观察,以后写结构体,尽量把占用内存较小的变量放在前面



欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/3002862.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)

保存