自定义类型知识详解(结构体,位段,枚举,联合体)

简介: 结构体是一些值的集合,这些值可以是不同类型的变量,被称为成员变量。

结构体

       定义,初始化,自引用

1:结构体的定义:

结构体是一些值的集合,这些值可以是不同类型的变量,被称为成员变量。

2:初始化:

三种初始化方式,被注释掉的代码只有在支持C99的编译器下才可以使用,他是乱序初始化,我们常用的是顺序初始化。

#include <stdio.h>
struct Student
{
  char name[8];
  int age;
  char sex[4];
}a = {"haha",4,"nan"};           //第一种
int main()
{
  struct Student c = { "gaga",6,"?" };   //第二种
  //struct Student b = { .age = 5, .name = "hehe", .sex = "nu" };   //第三种
  return 0;
}


这里还有一种特殊的声明方式:

a02e5756794942bc8f7cccb86135c6d9.png

这样声明的话只能在这里进行初始化,因为他没有名字,后面再想使用这个结构体,没有名字就无法创建该类型变量。


3:结构体的自引用:


这是错误用例,如果这样写的话,那么sizeof(struct Student)大小是多少呢?这样是算不出来的。

8b838fce16944e08aa4eb481f78e03df.png


接下来我们看正确用例:


这样,就是一个指针,指向这个结构体类型,就不会出现上面无限套娃的情况,再往后面加其实就是一个链表了。

377093a971e64cac8d296473050e0ddd.png


再看重命名后的错误用例:

53ce8cac48fc4111ac90fcae51ff737d.png


Stu前面的代码(typedef是重命名,不算在类型里)都是这个结构体类型,也就是说Stu在结构体里是未定义的,然后去使用Stu,这是错误的,改正:


86eae6ed3cb342aabdd8d00b91d06803.png

  内存对齐,修改默认对齐数

这里我们将说到结构体的大小计算,涉及到结构体的内存对齐。


我们先看一下这个结构体的大小,各位也可以先计算一下,看看结果是否正确。


eaf4a64a121847ffbcfcb25e840d6798.png

正确答案是:12个字节,你算对了吗?


结构体对齐规则:

       1:第一个成员变量在与结构体偏移量为0的位置。

5052d4d6868c486f91e21a2fe6fb9d5c.png

2:每个成员变量要对齐到他们各自的对齐数的整数倍处,也就是说偏移量是其对齐数的整数倍,起始偏移量为0,对齐数的大小为其sizeof的大小与默认对齐数比较后取小者。

3:每个编译器都有默认对齐数,VS为8个字节,计算结构体总大小时要看其最大对齐数,取成员变量中最大的对齐数,在每个成员变量各自对齐后,结构体所占内存总大小是将总和补到最大对齐数的整数倍。

4:如果嵌套了结构体,则嵌套的结构体的最大对齐数为其自身的最大对齐数,对齐到自身最大对齐数的整数倍处。


接下来我们来计算这个案例


char ch 占一个字节,VS默认对齐数为8,取小,对齐数为1, 放在开头,int a占4个字节,比8小,对齐数取4,4偏移量是4的倍数,从4位置开始分配四个字节的内存空间,short同理,8是2的整数倍,从该处分配内存空间。


结构体中成员变量的对齐数中最大为4,与默认对齐数8相比取4,最大对齐数为4,总大小为4的倍数,则取12个字节。图中占用字节为7个字节,浪费5个字节,但这是有意义的

dc9caec8c8644cf5bff05099a51df269.png

内存对齐的意义:

1:平台的移植性:不是每个平台都能访问任意位置的地址上的任意数据。

2:性能原因:

6cc4806ec47a4391b57fa423e049e59a.png

总的来说,结构体内存对齐就是用空间换取时间的做法


       传参:

结构体传参最好传指针,如果传值,实参的临时拷贝对栈区的压力会增大。

#include <stdio.h>
#include <string.h>
typedef struct T
{
  int a;
  char b;
}T;
void Test(T* stu)
{
  printf("haha:%d", stu->a);
}
int main()
{
  T stu;
  memset(&stu, 0, sizeof(T));
  Test(&stu);
  return 0;
}

位段

1:位段成员必须是int ,signed int,unsigned int(char也可以)

2:位段成员名后面有一个冒号和数字(分配的bit位)

8e26389dd07a43b980d329b25fb32c43.png

A就是一个位段类型。


       位段内存问题

位段涉及很多问题,比如读取数据的方向,空间不足时数据存部分还是不存。

位段的空间开辟是按照开辟四个字节(int),或一个字节(char)的方式

5dcf79ba11824fc9a4b195e32a94e72b.png

      跨平台及其应用问题

a1d1eb05c0dd4e0188c5b461bbf29c9c.png

image.png

枚举

       定义,初始化

enum A
{
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
};

使用enum创建枚举类型,从第一个枚举常量开始,若不给定值,默认为0,后面的枚举常量依次加1,我们看结果:

image.png

当然,我们也可以给枚举常量赋初值:

image.png

 实战举例使用及其优点

2c1d8665ec464069840c6052e8017d18.png

只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。

a6208bc20e694d34af6c9091927abfa5.png

当然,硬要强制转换也是可以的,不推荐。


一般来说我们也不这么用,真正要用是在switch语句中使用,每一个case后的常量换成枚举常量。

比如:

case 0:我们可以换成case Add:(假设Add是枚举常量的第一个)


这样我们看起来就会方便一些,不会说一看0,不清楚这是做什么的。


联合体

       联合体定义:

一种特殊的自定义类型,接下来看声明:

dca234551aac4dfca1b88797bf9c1b8d.png

      特点:

所有成员共用同一块空间,他们的起始地址都相同:

c72a291258da45cc967b4ec45982c5e7.png

一个union联合体的大小,至少是最大成员的大小。


       大小计算问题

整体大小也要对齐到最大对齐数的整数倍,上面举例的联合体大小为4个字节。

目录
相关文章
|
编译器 Linux C语言
【C语言】自定义类型:结构体(内存对齐),枚举,联合
【C语言】自定义类型:结构体(内存对齐),枚举,联合
|
编译器 C语言
C语言自定义类型 — 结构体、位段、枚举、联合
本期主要对通讯录三篇博客文章进行补充 通讯录文章:通讯录系列文章 对结构体进行详细介绍,其次讲解位段、枚举、联合体
94 0
|
1月前
|
存储 编译器 C语言
自定义类型(二)结构体位段,联合体,枚举
本文介绍了C++中结构体的默认对齐数修改,位段的声明和使用,联合体的概念及其实际应用,以及枚举类型的用途。通过实例展示了如何优化内存使用和提高代码可读性。
16 1
|
6月前
|
存储 C语言
C语言进阶⑮(自定义类型)(结构体+枚举+联合体)(结构体实现位段)(下)
C语言进阶⑮(自定义类型)(结构体+枚举+联合体)(结构体实现位段)
42 0
|
6月前
|
存储 编译器 程序员
C语言:自定义类型 - 结构体 & 联合体 & 枚举
C语言:自定义类型 - 结构体 & 联合体 & 枚举
46 2
|
6月前
|
存储 编译器 C语言
【C语言】自定义类型 -- -- 结构体、位段、枚举、联合体
【C语言】自定义类型 -- -- 结构体、位段、枚举、联合体
23 0
|
6月前
|
存储 C语言
C语言进阶⑮(自定义类型)(结构体+枚举+联合体)(结构体实现位段)(中)
C语言进阶⑮(自定义类型)(结构体+枚举+联合体)(结构体实现位段)
36 0
|
6月前
|
编译器 C语言 C++
C语言进阶⑮(自定义类型)(结构体+枚举+联合体)(结构体实现位段)(上)
C语言进阶⑮(自定义类型)(结构体+枚举+联合体)(结构体实现位段)
22 0
|
6月前
|
存储
【自定义类型详解】完结篇—联合体(共用体)与枚举详解
【自定义类型详解】完结篇—联合体(共用体)与枚举详解
42 0
|
6月前
|
存储 编译器 C语言
超全超详细的C语言结构体、位段、枚举、联合体详解
超全超详细的C语言结构体、位段、枚举、联合体详解