目录
- 一:枚举
- 1.1:枚举类型的定义:
- 1.1:枚举的优点:
- 1.2:枚举的使用:
- 二:联合(共用体)
- 2.1:联合类型的定义:
- 2.2:联合类型的特点:
- 2.3:联合大小的计算:
- 2.4:判断当前机器的大小端:
前言:
之前我们已经学过了自定义类型中的 结构体,忘了的伙计可以再去看看。今天我们继续学习自定义类型中的另外两个成员—— 枚举和 联合
一:枚举
枚举顾名思义就是 一 一 列举。把可能的取值 一 一 列举出来,比如我们现实生活中的:一周的星期一到星期日是有限的7天,可以 一 一列举;性别有:男、女、保密,也可以 一 一 列举;月份有12个月也可以 一 一 列举。向这些可以 一 一 列举的数据,我们就可以把他们定义成枚举类型
1.1:枚举类型的定义:
enum Sex//括号里面添加的是枚举未来的可能取值,这些取值是不能被修改的,因此我们把这些值叫做枚举常量
{
MALE,
FEMALE,
SECRET
};
//这就是声明了一个枚举类型
int main()
{
enum Sex s;//定义一个枚举变量
return 0;
}
上面代码中的 enum Sex
就是我们定义出来的一个枚举类型。我们发现它和结构体的声明十分相似,但也有不同的地方,比如:结构体中的关键字是 struct
而枚举的关键字是 enum
;结构体的 { }
里面是结构体的成员列表,而枚举的 { }
里是枚举未来的可能取值,这些值是不能被修改的,因此我们也把这些值叫做枚举常量;最后一点不同在于,结构体的每一个成员后边都要加 :
隔开,而枚举常量之间是用 ,
加以区分。
枚举常量的取值:
通过打印我们发现,枚举常量的默认取值是从
0
0
0 开始的,每次递增
1
1
1 。当然我们在定义枚举类型的时候,也可以给他们赋初值
此时这三个枚举常量的值就不再是
0
0
0 、
1
1
1 、
2
2
2 了,而是我们初始化赋值的
1
1
1 、
2
2
2 、
4
4
4 。如果只初始化枚举常量其中的某一个,后面的枚举常量会在当前初始化值的基础上,每次自增
1
1
1
如上,我们只初始化了枚举常量中的 FEMALE
给它赋值成
2
2
2 ,MALE
还是默认的从
0
0
0 开始,SECRET
则是在它前一个枚举常量 FEMALI
2
2
2 的基础上自增了
1
1
1 。
注意:
枚举类型的变量在赋值的时候,最好用枚举常量来赋值
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个字节。下面我们通过代码来测试一下。
可以看出一个枚举类型的大小确实就是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
8
8 ,说明这个来联合体的大小是
8
8
8 个字节,为什么呢?这是因为,我们在计算的时候忽略了上面联合体大小计算的第二点 —— 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。我们来分析一下这个联合体的大小,首先对数组 c
来说,它的对齐数是从数组一个成员的大小和默认对齐数中取较小的,这里 c
数组是 char
型,所以它的一个成员的大小是
1
1
1 个字节,vs中默认对齐数是
8
8
8 ,故数组 c
的对齐数应该是
1
1
1 ,i
的对齐数是从它自身大小和默认对齐数中取最小的,也就是
4
4
4 ,综上,联合的最大成员,也就是 c
的大小是
5
5
5 个字节,联合中的最大对齐数是
4
4
4 ,显然
5
5
5 不是
4
4
4 的倍数,因此来联合的大小就要对齐到最大对齐数的整数倍,
5
5
5 接下来,只有
8
8
8 是最大对齐数
4
4
4 的倍数,因此最终这个联合体的大小是
8
8
8 个字节。
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
1
1 ,再用联合体里的 char
型变量取访问,因为联合体的成员共用一块空间,且 char
型变量,一次只能访问一个字节,我们就可以通过看 char
类型访问到的是不是
1
1
1 来判断大小端,就不用再像方法一那样,取 int
型的地址,然后再强制转换成 char*
的指针,再去解引用。方法二就显得比较巧妙。
总结:
今天的分享到这里就结束了,今天我们学习了枚举和联合体这两个自定义类型,知道了如何定义一个枚举变量以及它相较于 #define
的优点。在联合体这一块,我们知道了它的最大特点,即联合体的成员会共用同一块空间,了解了一个联合体大小的计算方法。
如果觉得对你有帮助的话,记得点赞、评论、收藏哟