【C语言】联合体与枚举类型
文章目录
前言
哈喽,各位小伙伴大家好!上期我们讲了C语言自定义类型中的结构体,今天我们继续补充剩下的联合体和枚举类型。话不多说,咱们进入正题,向大厂冲锋!
一.联合体类型
1.1联合体类型的声明
像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以是不同的类型。
但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫:共用体。
那联合体如何声明呢?
- 关键字
联合的也有关键字,关键字为union.
union Un//union关键字
{
char c;
int i;
};
那联合体的大小是怎么计算的呢?
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = { 0 };
//计算联合体变量的⼤⼩
printf("%d\n", sizeof(un));
return 0;
}
以这个联合体为例,大家觉得联合体大小是多少?
可能很多小伙伴会觉得是 1+4=5 大小就是5个字节。
但其实这里面另有玄机。
答案是4,至于为啥是4,就涉及到了联合体的特点了。
1.2联合体的特点
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = { 0 };
// 下⾯输出的结果是⼀样的吗?
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
printf("%p\n", &un);
return 0;
}
大家先看一段代码,我们创建一个联合体
然后分别取出联合体的成员i 联合体的成员c 联合体的地址。
然后我们发现这三个的地址一模一样。
那我们再画出他们内存中的图看看。
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
所以联合体也叫共用体。
注意事项:
- 相互影响
因为他们是共用的,所以修改一个成员时也会修改其他成员。他们是相互影响的。 - 单独使用
所以,同一时间只能用一个成员,否则会产生冲突。
因为共用了同一个字节,所以这个联合体大小就是4字节。
这里我们还能验证一下联合体的共用:
/联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = {0};
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);
return 0;
}
这里我们分别给i和c赋值。
前面我们说了他们共用,所以修改会相互影响,是不是呢?
这里我们给i赋值为0x11223344,因为小端存放,所以在内存到这存放。
这里我们又给c赋值,因为i和c共用一个字节,所以i也会被修改。
那好像联合体的大小就是最大成员的大小。
那以后计算联合体大小是只需要找到就大成员的的大小就可以了?
答案是否定的。为什么呢?
1.3联合体大小的计算
联合体的计算规则有两个:
- 联合的大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
#include <stdio.h>
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
int main()
{
//下⾯输出的结果是什么?
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
return 0;
}
按照我们的计算规则来算:
那是不是呢?
是的,联合体计算的规则就是按照我们说的计算。
所以,可不敢单纯的把联合最大成员当成联合体的大小计算。
1.4相同成员的结构体和联合体对比
那联合体到底有什么用呢?
struct S
{
char c;
int i;
};
struct S s = {0};
union Un
{
char c;
int i;
};
union Un un = {0};
我们再对比一下相同成员的结构体和联合体的内存布局情况。
观察到这里我们可以得出结论:
结论:使用联合体可以节省内存
是的,联合体就是用来节省内存空间的。
那具体如何使用联合体来节省内存空间呢?
1.5联合体的应用
使用联合体是可以节省空间的,举例:
比如,我们要搞一个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯子、衬衫。
每一种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
- 其他信息
图书:书名、作者、页数
杯子:设计
衬衫:设计、可选颜色、可选尺寸
那我们不耐心思考,直接写出一下结构:
struct gift_list
{
//公共属性
int stock_number;//库存量
double price; //定价
int item_type;//商品类型
//特殊属性
char title[20];//书名
char author[20];//作者
int num_pages;//⻚数
char design[30];//设计
int colors;//颜⾊
int sizes;//尺⼨
};
上述的结构其实设计的很简单,用起来也方便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的大小就会偏大,比较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常用的。比如:
商品是图书,就不需要design、colors、sizes。
既然书,杯子,衬衫这三种物品不会同时使用。
那我们就把三个物品的特殊属性作为成员放在一个联合体中。
把公共属性单独写出来,剩余属于各种商品本身的属性使用联合体起来,这样就可以
节省所需的内存空间,一定程度上节省了内存。
struct gift_list
{
int stock_number;//库存量
double price; //定价
int item_type;//商品类型
union{
struct
{
char title[20];//书名
char author[20];//作者
int num_pages;//⻚数
}book;
struct
{
char design[30];//设计
}mug;
struct
{
char design[30];//设计
int colors;//颜⾊
int sizes;//尺⼨
}shirt;
}item;
};
这样就可以节省内存空间了。
1.6联合体练习
写一个程序,用联合体判断当前机器是大端?还是小端?
一般我们判断大小端的思路是这样的:
因此只需判断低地址出的字节内容即可。
union Un
{
char c;
int i;
};
我们又知道这个联合体是共用的。
所以我们就可以这样写:
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;//返回1是⼩端,返回0是⼤端
}
二.枚举类型
2.1枚举类型的声明
接下来我们讲最后一个自定义类型:
枚举顾名思义就是一一列举。
把可能的取值一一列举。
比如我们现实生活中:
一周的星期⼀到星期日是有限的7天,可以⼀⼀列举
性别有:男、女、保密,也可以⼀⼀列举
月份有12个月,也可以⼀⼀列举
三原色,也是可以意义列举
- 关键字
枚举的关键字是enum
这些数据的表示就可以使用枚举了
enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
enum Color//颜⾊
{
RED,
GREEN,
BLUE
};
- 枚举变量的创建
enum Color color=RED;
{}中的内容是枚举类型的可能取值,也叫枚举常量 。
那他们的值是什么呢?
enum Color//颜⾊
{
RED,
GREEN,
BLUE
};
int main()
{
enum Color color;
printf("%d\n", RED);
printf("%d\n", GREEN);
printf("%d\n", BLUE);
return 0;
}
这些可能取值都是有值的,默认从0开始,依次递增1。
那我们能不能修改他们的值呢?
- 错误方式
注意枚举类型枚举的是常量,不能再定义后修改他的值。
- 正确
所以我们只能在创建枚举变量时给一个初始值
enum Color//颜⾊
{
RED=5,
GREEN,
BLUE
};
enum Color//颜⾊
{
RED,
GREEN=5,
BLUE
};
那现在他们的值又是什么呢?
初始默认值为0,后面修改的话,从修改值开始向下递增。
2,2枚举类型的优点
enum Color//颜⾊
{
RED,
GREEN=5,
BLUE
};
#define RED
#define GREEN
#define BLUE
枚举是拿来定义常量的,那我们前面学过的#define也是定义常量。他们有什么区别吗?
- 增加代码的可读性和可维护性
void menu()
{
printf("*** 1.ADD 2.SUB ***");
printf("*** 3.MUL 4.DIV ***");
printf("*** 0.EXIT ***");
}
int main()
{
int input;
scanf("%d", &input);
switch (input)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
break;
}
}
我们之前写计算器时是这样写的,大家发现我们需要对照着菜单才能知道具体代码的功能。这时我们就可以用枚举变量来增加代码的可读性。
enum Color//颜⾊
{
EXIT,
ADD,
SUB,
MUL,
DIV,
};
int main()
{
int input;
scanf("%d", &input);
switch (input)
{
case ADD:
break;
case SUB:
break;
case MUL:
break;
case DIV:
break;
default:
break;
}
}
这时我们就不用对照菜单就可以知道,具体代码是实现什么功能。
-
和#define定义的标识符比较枚举有类型检查,更加严谨。
枚举类型的变量是有类型,而#define定义的标识符是没有类型的。 -
便于调试,预处理阶段会删除 #define 定义的符号
-
使用方便,⼀次可以定义多个常量
-
枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
那是否可以拿整数给枚举变量赋值呢?在C语言中是可以的,但是在C++是不行的,C++的类型检查比较严格。
后言
这就是联合体和枚举类型啦,到这里我们已经学完了全部自定义类型。自定义类型可以让我们的代码更加灵活,我们可以多加使用并且熟练掌握自定义类型。今天就分享到这里,感谢各位小伙伴的垂阅,咱们下期见!拜拜~