【C语言】联合体与枚举类型

【C语言】联合体与枚举类型

个人主页:大白的编程日记
个人专栏: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++的类型检查比较严格。


后言

这就是联合体和枚举类型啦,到这里我们已经学完了全部自定义类型。自定义类型可以让我们的代码更加灵活,我们可以多加使用并且熟练掌握自定义类型。今天就分享到这里,感谢各位小伙伴的垂阅,咱们下期见!拜拜~

  • 72
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 83
    评论
评论 83
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大白的编程日记.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值