💓博主CSDN主页:杭电码农-NEO💓
⏩专栏分类:C语言学习分享⏪
🚚代码仓库:NEO的学习日记🚚
🌹关注我🫵带你学习更多C语言知识
🔝🔝
1. 前言
文章目标:
本篇文章着重给大家讲解:
结构体内存对齐的知识
并且介绍位段,联合的内容
最后对这一板块做出拓展
结构体,位段和联合
这哥几个的区别和关联到底是什么?
2. 结构体内存大小问题
首先看下面这段代码:
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
这两个结构体中存放的都是
两个char类型和一个int类型数据
所以暂时推断出它们所占内存空间相同
那它们占了多大的空间呢?
char+char+int ,1+1+4=6
我们推断出它至少占6个字节
来验证一下:
printf("%d\n",sizeof(struct S1));
printf("%d\n",sizeof(struct S2));
结果很出乎意料,它们不仅内存不相同
并且和我们推断出的6也没有太大关系!
3. 结构体内存对齐规则
出现以上原因是因为:
结构体有特殊的内存对齐规则:
3.1 偏移量的概念
在这儿之前先介绍偏移量的概念:
3.2 内存对齐规则
对齐规则:
- 第一个成员在偏移量为0的地址处
- 其他成员要对齐到对齐数的整数倍处
- 结构体总大小为成员中最大对齐数的整数倍
对齐数概念:
对齐数 = min(编译器默认的对齐数 , 该成员大小)
- VS中默认对齐数为8
- linux中对齐数就是成员自身大小
比如在VS编译环境下:
struct S3
{
double d;
char c;
int i;
};
- d的大小是8,默认对齐数是8,对齐数就是8
- c的大小是1,默认对齐数是8,对齐数就是1
- i的大小是4,默认对齐数是8,对齐数就是4
3.3 内存对齐规则实例分析
拿上面的例子来分析
struct S3
{
double d;
char c;
int i;
};
已知:d的对齐数为8
c的对齐数位1
i的对齐数为4
结构体最大对齐数为8
可得:
对于图片的解释:
d是第一个成员,所以它直接放在
偏移量为0的位置
- c是第二个成员,它的对齐数是1
任何一个数都是1的倍数,所以
c紧接着放在d内存的后面
- i 是第三个成员,它的对齐数是4
而c的后面是9,9不是4的倍数
10也不是4的倍数,直到12才是
4的倍数,所以i从12开始放
- 最后一个成员放完后的位置是15
而结构体最大对齐数是8
15不是8的倍数,16才是
所以最终在16停止
3.4 回头验证最初的数据
最开始的两个结构体:
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
1. 对于S1而言是这样的情况:
这也就解释清楚为什么会打印12出来的
对于S2而言是这样的情况:
占8个字节也解释清楚了!
4. 存在内存对齐规则的原因
- 原因1:平台原因(移植原因):
不是所有的硬件平台都能访问
任意地址上的任意数据的
某些硬件平台只能在某些地址处取
某些特定类型的数据,否则抛出硬件异常
- 原因2:性能原因
如果不存在内存对齐的话
平台4个字节4个字节的访问
int类型的数据时有可能需要
读取两次才能取到一个数据
总的来说:内存对齐是拿空间换取时间
- 节省空间的技巧:
发现同样的成员类型和数量
成员放的位置不同,结构体大小
也存在的很大的区别
在写结构体时尽量将占用空间
小的数据集中在一起能节省空间
5. 位段
位段和结构的声明非常相似
但又存在下面这两个不同:
- 位段的成员必须是整型家族(int/char)
- 位段的成员名后边有一个冒号和一个数字
比如:定义一个位段
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
这个位段的大小是多少呢?
肯定不会是4×4=16个字节这么简单
printf("%d\n",sizeof(struct A));
结果是8,我们来简单分析一下:
5.1 位段的内存分配规则
位段是具有不确定性的,不能跨平台
它在每一个编译器下可能有所不同
我只介绍在VS编译器下的具体规则:
先初始化一下结构体:
s.a = 3;
s.b = 12;
s.c = 3;
s.d = 4;
冒号后面的数字代表
变量所占的二进制位(比特位)
画图解释:
6. 联合(共用体)
联合也是一种自定义类型
它其中的变量共用同一份空间!
它的不同:
- 成员共用同一份空间
- 不用struct定义,而是用union定义
- 联合的大小至少是最大成员的大小
比如:
union Un
{
int i;
char c;
};
union Un un;//定义联合变量
printf("%d\n", &(un.i));//它们的地址相同
printf("%d\n", &(un.c));//共用同一份空间
6.1 联合大小计算
联合共用体和结构体一样
有内存对齐原则,不过联合的比较简单:
- 联合的大小至少是最大成员的大小
- 最大成员大小不是最大对齐数的整数倍时
就要对齐到最大对齐数的整数倍
比如:下面这两个联合
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
它们的大小分别是:8和16
7. 总结以及拓展
结构体的内存对齐是面试的常考点!
掌握它不仅可以更深层次了解C语言
还可以在面试的时候给面试官一个震撼
拓展:修改默认对齐数
C语言提供了#pragma指令
来帮助我们解决这个问题:
假设我们想要将默认对齐数改为1:
#pragma pack(1)//设置默认对齐数为1
struct S1
{
char c1;
int i;
char c2;
};
假设我们又想修改回来:
#pragma pack(1)//设置默认对齐数为1
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
struct S2
{
char c1;
int i;
char c2;
};
上述代码中,结构体S1
使用的是默认对齐数为1
而S2使用的默认对齐数是8
拓展:利用联合求大小端文章来源:https://www.toymoban.com/news/detail-554199.html
详细可以参考以下这篇文章:
利用联合体判断机器大小端文章来源地址https://www.toymoban.com/news/detail-554199.html
到了这里,关于【C语言进阶(七)】自定义类型--结构体,位段,联合的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!