【C语言进阶(七)】自定义类型--结构体,位段,联合

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:C语言学习分享

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习更多C语言知识

🔝🔝



自定义类型

  • [1. 前言](#1. 前言 "#1__14")
  • [2. 结构体内存大小问题](#2. 结构体内存大小问题 "#2__28")
  • [3. 结构体内存对齐规则](#3. 结构体内存对齐规则 "#3__71")
    • [3.1 偏移量的概念](#3.1 偏移量的概念 "#31__77")
    • [3.2 内存对齐规则](#3.2 内存对齐规则 "#32__82")
    • [3.3 内存对齐规则实例分析](#3.3 内存对齐规则实例分析 "#33__111")
    • [3.4 回头验证最初的数据](#3.4 回头验证最初的数据 "#34__154")
  • [4. 存在内存对齐规则的原因](#4. 存在内存对齐规则的原因 "#4___187")
  • [5. 位段](#5. 位段 "#5__215")
    • [5.1 位段的内存分配规则](#5.1 位段的内存分配规则 "#51__248")
  • [6. 联合(共用体)](#6. 联合(共用体) "#6__272")
    • [6.1 联合大小计算](#6.1 联合大小计算 "#61__301")
  • [7. 总结以及拓展](#7. 总结以及拓展 "#7__335")

1. 前言

文章目标:

本篇文章着重给大家讲解:
结构体内存对齐的知识
并且介绍位段,联合的内容
最后对这一板块做出拓展

结构体,位段和联合
这哥几个的区别和关联到底是什么?


2. 结构体内存大小问题

首先看下面这段代码:

c 复制代码
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个字节

来验证一下:

c 复制代码
printf("%d\n",sizeof(struct S1));
printf("%d\n",sizeof(struct S2));

结果很出乎意料,它们不仅内存不相同
并且和我们推断出的6也没有太大关系!


3. 结构体内存对齐规则

出现以上原因是因为:
结构体有特殊的内存对齐规则:


3.1 偏移量的概念

在这儿之前先介绍偏移量的概念:


3.2 内存对齐规则

对齐规则:

  1. 第一个成员在偏移量为0的地址处
  2. 其他成员要对齐到对齐数的整数倍处
  3. 结构体总大小为成员中最大对齐数的整数倍

对齐数概念:

对齐数 = min(编译器默认的对齐数 , 该成员大小)

  • VS中默认对齐数为8
  • linux中对齐数就是成员自身大小

比如在VS编译环境下:

c 复制代码
struct S3
{
	double d;
	char c;
	int i;
};
  • d的大小是8,默认对齐数是8,对齐数就是8
  • c的大小是1,默认对齐数是8,对齐数就是1
  • i的大小是4,默认对齐数是8,对齐数就是4

3.3 内存对齐规则实例分析

拿上面的例子来分析

c 复制代码
struct S3
{
  double d;
  char c;
  int i;
};

已知:d的对齐数为8
c的对齐数位1
i的对齐数为4
结构体最大对齐数为8

可得:

对于图片的解释:

  1. d是第一个成员,所以它直接放在
    偏移量为0的位置
  2. c是第二个成员,它的对齐数是1
    任何一个数都是1的倍数,所以
    c紧接着放在d内存的后面
  3. i 是第三个成员,它的对齐数是4
    而c的后面是9, 9不是4的倍数
    10也不是4的倍数,直到12才是
    4的倍数,所以i从12开始放
  4. 最后一个成员放完后的位置是15
    而结构体最大对齐数是8
    15不是8的倍数,16才是
    所以最终在16停止

3.4 回头验证最初的数据

最开始的两个结构体:

c 复制代码
struct S1
{
	char c1;
	int i;
	char c2;
};

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

1. 对于S1而言是这样的情况:

这也就解释清楚为什么会打印12出来的

对于S2而言是这样的情况:


占8个字节也解释清楚了!


4. 存在内存对齐规则的原因

  1. 原因1:平台原因(移植原因):

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

  1. 原因2:性能原因

如果不存在内存对齐的话
平台4个字节4个字节的访问
int类型的数据时有可能需要
读取两次才能取到一个数据

总的来说:内存对齐是拿空间换取时间

  1. 节省空间的技巧:

发现同样的成员类型和数量
成员放的位置不同,结构体大小
也存在的很大的区别

在写结构体时尽量将占用空间
小的数据集中在一起能节省空间


5. 位段

位段和结构的声明非常相似

但又存在下面这两个不同:

  1. 位段的成员必须是整型家族(int/char)
  2. 位段的成员名后边有一个冒号和一个数字

比如:定义一个位段

c 复制代码
struct A
{
  int _a:2;
  int _b:5;
  int _c:10;
  int _d:30;
};

这个位段的大小是多少呢?
肯定不会是4×4=16个字节这么简单

c 复制代码
printf("%d\n",sizeof(struct A));

结果是8,我们来简单分析一下:


5.1 位段的内存分配规则

位段是具有不确定性的,不能跨平台
它在每一个编译器下可能有所不同

我只介绍在VS编译器下的具体规则:

先初始化一下结构体:

c 复制代码
s.a = 3;
s.b = 12;
s.c = 3;
s.d = 4;

冒号后面的数字代表
变量所占的二进制位(比特位)

画图解释:


6. 联合(共用体)

联合也是一种自定义类型
它其中的变量共用同一份空间!

它的不同:

  1. 成员共用同一份空间
  2. 不用struct定义,而是用union定义
  3. 联合的大小至少是最大成员的大小

比如:

c 复制代码
union Un
{
  int i;
  char c;
};
union Un un;//定义联合变量
printf("%d\n", &(un.i));//它们的地址相同
printf("%d\n", &(un.c));//共用同一份空间

6.1 联合大小计算

联合共用体和结构体一样

有内存对齐原则,不过联合的比较简单:

  • 联合的大小至少是最大成员的大小
  • 最大成员大小不是最大对齐数的整数倍时
    就要对齐到最大对齐数的整数倍

比如:下面这两个联合

c 复制代码
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:

c 复制代码
#pragma pack(1)//设置默认对齐数为1

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

假设我们又想修改回来:

c 复制代码
#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


拓展:利用联合求大小端

详细可以参考以下这篇文章:
利用联合体判断机器大小端


🔎 下期预告:动态内存管理 🔍

相关推荐
玉树临风江流儿1 小时前
Linux驱动开发(速记版)--设备模型
linux·驱动开发
杰哥在此1 小时前
Python知识点:如何使用Multiprocessing进行并行任务管理
linux·开发语言·python·面试·编程
枫叶丹43 小时前
【在Linux世界中追寻伟大的One Piece】进程信号
linux·运维·服务器
刻词梨木3 小时前
ubuntu中挂载点内存不足,分配不合理后使用软链接的注意事项
linux·运维·ubuntu
灯火不休ᝰ4 小时前
[win7] win7系统的下载及在虚拟机中详细安装过程(附有下载文件)
linux·运维·服务器
powerfulzyh8 小时前
Ubuntu24.04远程开机
linux·ubuntu·远程工作
ulimpid8 小时前
Command | Ubuntu 个别实用命令记录(新建用户、查看网速等)
linux·ubuntu·command
HHoao8 小时前
Ubuntu启动后第一次需要很久才能启动GTK应用问题
linux·运维·ubuntu
小灰兔的小白兔8 小时前
【Ubuntu】Ubuntu常用命令
linux·运维·ubuntu
GFCGUO8 小时前
ubuntu18.04运行OpenPCDet出现的问题
linux·python·学习·ubuntu·conda·pip