【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语言中实现结构体的深拷贝,需要手动分配内存并逐个复制成员变量,确保新结构体与原结构体完全独立,避免浅拷贝导致的数据共享问题。具体方法包括使用 `malloc` 分配内存和 `memcpy` 或手动赋值。
62 10
|
2月前
|
安全 编译器 Linux
【c语言】轻松拿捏自定义类型
本文介绍了C语言中的三种自定义类型:结构体、联合体和枚举类型。结构体可以包含多个不同类型的成员,支持自引用和内存对齐。联合体的所有成员共享同一块内存,适用于判断机器的大小端。枚举类型用于列举固定值,增加代码的可读性和安全性。文中详细讲解了每种类型的声明、特点和使用方法,并提供了示例代码。
27 3
|
2月前
|
存储 大数据 编译器
C语言:结构体对齐规则
C语言中,结构体对齐规则是指编译器为了提高数据访问效率,会根据成员变量的类型对结构体中的成员进行内存对齐。通常遵循编译器默认的对齐方式或使用特定的对齐指令来优化结构体布局,以减少内存浪费并提升性能。
|
2月前
|
编译器 C语言
共用体和结构体在 C 语言中的优先级是怎样的
在C语言中,共用体(union)和结构体(struct)的优先级相同,它们都是用户自定义的数据类型,用于组合不同类型的数据。但是,共用体中的所有成员共享同一段内存,而结构体中的成员各自占用独立的内存空间。
|
2月前
|
存储 C语言
C语言:结构体与共用体的区别
C语言中,结构体(struct)和共用体(union)都用于组合不同类型的数据,但使用方式不同。结构体为每个成员分配独立的内存空间,而共用体的所有成员共享同一段内存,节省空间但需谨慎使用。
|
2月前
|
编译器 C语言 C++
C语言结构体
C语言结构体
29 5
|
2月前
|
编译器 Linux C语言
C语言 之 结构体超详细总结
C语言 之 结构体超详细总结
22 0
|
2月前
|
存储 编译器 Linux
深入C语言:探索结构体的奥秘
深入C语言:探索结构体的奥秘
|
2月前
|
存储 编译器 C语言
c语言回顾-结构体(2)(下)
c语言回顾-结构体(2)(下)
31 0
|
2月前
|
存储 编译器 程序员
c语言回顾-结构体(2)(上)
c语言回顾-结构体(2)(上)
31 0