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

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

对于图片的解释:

  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 回头验证最初的数据

最开始的两个结构体:

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. 位段的成员名后边有一个冒号和一个数字

比如:定义一个位段

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. 联合(共用体)

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

它的不同:

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

比如:

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


拓展:利用联合求大小端

详细可以参考以下这篇文章:

利用联合体判断机器大小端


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


相关文章
|
2月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
11天前
|
存储 安全 编译器
C语言自定义类型
C语言自定义类型
22 10
|
5天前
|
存储 C语言
C语言------结构体和共用体
这篇文章是关于C语言中结构体和共用体的实训,通过示例代码演示了结构体的定义、赋值、使用,以及如何使用结构体变量进行数据的组织和操作,包括输入、排序、求平均分和查找学生信息等功能。
C语言------结构体和共用体
|
2月前
|
网络协议 编译器 Linux
结构体(C语言)
结构体(C语言)
|
1月前
|
存储 编译器 定位技术
结构体数组在C语言中的应用与优化策略
结构体数组在C语言中的应用与优化策略
|
1月前
|
存储 编译器 数据库
结构体数组在C语言中的应用与优化技巧
结构体数组在C语言中的应用与优化技巧
|
2月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
29 0
|
2月前
|
C语言
C语言中的结构体
C语言中的结构体
17 0
|
2月前
|
编译器 C语言 C++
【海贼王编程冒险 - C语言海上篇】自定义类型:结构体,枚举,联合怎样定义?如何使用?
【海贼王编程冒险 - C语言海上篇】自定义类型:结构体,枚举,联合怎样定义?如何使用?
18 0
|
2月前
|
存储 C语言 C++
【C语言刷题系列】水仙花数的打印及进阶
【C语言刷题系列】水仙花数的打印及进阶