【C语言进阶】枚举与联合体

简介: 【C语言进阶】枚举与联合体

148971e7d6374683aa19ccc3c55859b6.gif

1039efd5e5ca47a4b97a5fe224c88be3.gif

前言:

 之前我们已经学过了自定义类型中的 结构体,忘了的伙计可以再去看看。今天我们继续学习自定义类型中的另外两个成员—— 枚举和 联合

一:枚举

枚举顾名思义就是 一 一 列举。把可能的取值 一 一 列举出来,比如我们现实生活中的:一周的星期一到星期日是有限的7天,可以 一 一列举;性别有:男、女、保密,也可以 一 一 列举;月份有12个月也可以 一 一 列举。向这些可以 一 一 列举的数据,我们就可以把他们定义成枚举类型

1.1:枚举类型的定义:

enum Sex//括号里面添加的是枚举未来的可能取值,这些取值是不能被修改的,因此我们把这些值叫做枚举常量
{
  MALE,
  FEMALE,
  SECRET
};
//这就是声明了一个枚举类型
int main()
{
  enum Sex s;//定义一个枚举变量
  return 0;
}

上面代码中的 enum Sex 就是我们定义出来的一个枚举类型。我们发现它和结构体的声明十分相似,但也有不同的地方,比如:结构体中的关键字是 struct 而枚举的关键字是 enum ;结构体的 { } 里面是结构体的成员列表,而枚举的 { } 里是枚举未来的可能取值,这些值是不能被修改的,因此我们也把这些值叫做枚举常量;最后一点不同在于,结构体的每一个成员后边都要加 : 隔开,而枚举常量之间是用 , 加以区分。

枚举常量的取值:

10b5de9498a640b49439469478f76c4d.png

通过打印我们发现,枚举常量的默认取值是从 0 00 开始的,每次递增 1 11 。当然我们在定义枚举类型的时候,也可以给他们赋初值

6e2945e1dc1c4c7b9aa4a785e888553b.png

此时这三个枚举常量的值就不再是 0 00 、1 11 、2 22 了,而是我们初始化赋值的 1 11 、2 22 、4 44 。如果只初始化枚举常量其中的某一个,后面的枚举常量会在当前初始化值的基础上,每次自增 1 11

2ffb6321f9a8463d8ed8f311f33637fa.png

如上,我们只初始化了枚举常量中的 FEMALE 给它赋值成 2 22 ,MALE 还是默认的从 0 00 开始,SECRET 则是在它前一个枚举常量 FEMALI 2 22 的基础上自增了 1 11 。

注意:

 枚举类型的变量在赋值的时候,最好用枚举常量来赋值

enum Sex//括号里面添加的是枚举未来的可能取值,这些取值是不能被修改的,因此我们把这些值叫做枚举常量
{
  MALE ,
  FEMALE = 2,
  SECRET 
};
//这就是声明了一个枚举类型
int main()
{
  enum Sex s = FEMALE;//最好是这样写
  enum Sex s = 2;//避免这样写 在C++中就会报错
  return 0;
}

1.1:枚举的优点:

  • 增加代码的可读性和可维护性
  • 和 #define 定义的标识符比较,枚举有类型检查,更加严谨
  • 防止了命名污染(封装)
  • 便于调试,#define在调试的时候会完成替换
  • 使用方便,一次可以定义多个变量

1.2:枚举的使用:

enum Color//颜色
{
 RED=1,
 GREEN=2,
 BLUE=4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5;//避免这样写

一个枚举类型变量的大小是?

 已给枚举变量,只会用来存放一个枚举常量的值,所以一个枚举变量的大小应该就是一个 int 的大小,也就是4个字节。下面我们通过代码来测试一下。

8aefadb8822f46c7b543066011b88957.png

可以看出一个枚举类型的大小确实就是4个字节。

二:联合(共用体)

2.1:联合类型的定义:

 联合也是一种特殊的自定义类型,它的关键字是 union 这种类型定义的变量也包含一系列的成员,特征是这些成员公用一块空间(所以联合也叫共用体)

union UN
{
  char c;
  int i;
};
//定义了一个共用体类型
int main()
{
  union UN un;//定义了一个共用体变量
  return 0;
}

2.2:联合类型的特点:

 联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员),受限于联合体的特点,我们不会同时使用联合里面的成员,只会用一种的一个。

union UN
{
  char c;
  int i;
};
//定义了一个共用体类型
int main()
{
  union UN un;//定义了一个共用体变量
  printf("%d\n", sizeof(un));
  printf("%p\n", &un);
  printf("%p\n", &un.c);
  printf("%p\n", &un.i);
  return 0;
}
//结果:
4
00EFFA2C
00EFFA2C
00EFFA2C

 可以看出,un、un.c、un.i它们三个的起始地址都一样,这也说明,联合体的成员确实会共用同一块内存空间。

2.3:联合大小的计算:

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

举个例子:

 大家猜猜下面这个联合体的大小是多少?

union Un1
{
  char c[5];
  int i;
};
int main()
{
  printf("%d\n", sizeof(union Un1));
  return 0;
}

这里我们可能会想,c 的大小是5个字节,i 的大小是1个字节,取较大的,所以算出这个联合体的大小是5个字节。但实际打印出来的确是 8 88 ,说明这个来联合体的大小是 8 88 个字节,为什么呢?这是因为,我们在计算的时候忽略了上面联合体大小计算的第二点 —— 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。我们来分析一下这个联合体的大小,首先对数组 c 来说,它的对齐数是从数组一个成员的大小和默认对齐数中取较小的,这里 c 数组是 char 型,所以它的一个成员的大小是 1 11 个字节,vs中默认对齐数是 8 88 ,故数组 c 的对齐数应该是 1 11 ,i 的对齐数是从它自身大小和默认对齐数中取最小的,也就是 4 44 ,综上,联合的最大成员,也就是 c 的大小是 5 55 个字节,联合中的最大对齐数是 4 44 ,显然 5 55 不是 4 44 的倍数,因此来联合的大小就要对齐到最大对齐数的整数倍,5 55 接下来,只有 8 88 是最大对齐数 4 44 的倍数,因此最终这个联合体的大小是 8 88 个字节。

2.4:判断当前机器的大小端:

方法一:

int main()
{
  int a = 1;// 00 00 00 01
  //假设左边低地址————————>右边高地址
  //小端存储 - - 01 00 00 00
  //大端存储 - - 00 00 00 01
  //因此我们可以通过判断首地址处的一个字节里面存放的是0还是1来判断大小端
  if (*(char*)&a == 1)
  {
    printf("小端\n");
  }
  else
  {
    printf("大端\n");
  }
  return 0;
}

方法二:

union Un
{
  char c;
  int i;
};
int main()
{
  union Un un;
  un.i = 1;
  if (un.c == 1)
  {
    printf("小端\n");
  }
  else
  {
    printf("大端\n");
  }
  return 0;
}

方法二这里就是利用了联合体的特性,先把联合体里的 int 型变量赋值成 1 11 ,再用联合体里的 char 型变量取访问,因为联合体的成员共用一块空间,且 char 型变量,一次只能访问一个字节,我们就可以通过看 char 类型访问到的是不是 1 11 来判断大小端,就不用再像方法一那样,取 int 型的地址,然后再强制转换成 char* 的指针,再去解引用。方法二就显得比较巧妙。

总结:

 今天的分享到这里就结束了,今天我们学习了枚举和联合体这两个自定义类型,知道了如何定义一个枚举变量以及它相较于 #define 的优点。在联合体这一块,我们知道了它的最大特点,即联合体的成员会共用同一块空间,了解了一个联合体大小的计算方法。

 如果觉得对你有帮助的话,记得点赞、评论、收藏哟


目录
相关文章
|
1天前
|
存储 编译器 程序员
C语言:自定义类型 - 结构体 & 联合体 & 枚举
C语言:自定义类型 - 结构体 & 联合体 & 枚举
12 2
|
1天前
|
存储 编译器 C语言
[C语言]自定义类型(结构体~枚举~联合体)
[C语言]自定义类型(结构体~枚举~联合体)
|
1天前
|
存储 C语言
C语言进阶---------作业复习
C语言进阶---------作业复习
|
1天前
|
存储 Linux C语言
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-2
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
1天前
|
自然语言处理 Linux 编译器
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-1
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
1天前
|
存储 编译器 C语言
C语言进阶第十课 --------文件的操作-1
C语言进阶第十课 --------文件的操作
|
1天前
|
存储 程序员 C语言
C语言进阶第九课 --------动态内存管理-2
C语言进阶第九课 --------动态内存管理
|
1天前
|
编译器 C语言
C语言进阶第九课 --------动态内存管理-1
C语言进阶第九课 --------动态内存管理
|
1天前
|
C语言
C语言进阶第八课 --------通讯录的实现
C语言进阶第八课 --------通讯录的实现
|
1天前
|
存储 算法 C语言
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)