自定义类型:联合和枚举

联合体

联合体类型的声明

联合体是由一个或者多个成员构成,可以有不同的类型。编译器只为最大的成员分配足够的内存空间,它的特点是所有成员共用同一块内存空间。联合体也叫共用体

union Data {
    int i;
    float f;
    char c;
};

联合体的特点:

联合体共用一块空间,联合变量的大小至少是联合体中最大成员的大小
验证联合体共用一块空间
代码如下:

union Un
{
	char a;
	int i;
};
int main()
{
	union Un un = { 0 };
	printf("%p\n", &un);//006FFB38
	printf("%p\n", &un.a);//006FFB38
	printf("%p\n", &un.i);//006FFB38
	return 0;
}

在内存中的布局
假设int占4个字节,float占4个字节,char占1个字节

0x00  0x01  0x02  0x03
[ i/f ][    ][    ][    ]  // i和f完全重叠
[ c ]                      // c占用首字节

给联合体其中⼀个成员赋值,其他成员的值也跟着变化,因此使用时注意在同一时间只能有效存储一个成员的值

union Un
{
	char c;
	int i;
};
int main()
{
	//联合体变量的定义
	union Un un = { 0 };
	un.i = 0x11223344;
	un.c = 0x55;//这里的55将前面的 44 33 22 11 中的 44改变成 55
	return 0;
}

示意图:
在这里插入图片描述

联合体和结构体的比较

struct S
{
	char a;
	int i;
};

union Un
{
	char a;
	int i;
};
 
int main()
{
	struct S s = { 0 };
	printf("%zd\n", sizeof(s));//8
	union Un un = { 0 };
	printf("%zd\n", sizeof(un));//4
	return 0;
}

二者在内存中的存储如下:
在这里插入图片描述

联合体大小的计算

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

下面是两个实际的案例:

union Un1
{
	//对齐数还是看它的类型
			   //最大成员大小
	char c[10];//10				1  8  1
	int i;//	 4				4  8  4
};
//最大成员的大小 = 元素的字节总数,比如上面的最大成员是char 占10个字节,不满足成员int 的最大对齐数,向后找12 是4的整数倍
union Un2
{
	short c[10];//20	2 8 2
	int i;//4			4 8 4
};

int main()
{
	printf("%zd\n", sizeof(union Un1));//12
	printf("%zd\n", sizeof(union Un2));//20
	return 0;
}

联合体的大小是最大成员的大小 这句话是错误的

联合体是可以节省空间的

联合体是可以节省空间的,但不是绝对的节省,他也会浪费一点空间的
那什么时候使用联合体呢,这里举个实际的案例:
使⽤联合体是可以节省空间的,举例:
⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。
每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。

图书:书名、作者、⻚数
杯⼦:设计
衬衫:设计、可选颜⾊、可选尺⼨

那我们耐⼼思考,直接写出⼀下结构:

联合体节省空间实例
struct gift_list
{
	int stock_number;//库存量
	double price;//定价
	int item_type;//商品类型

	char title[20];//书名
	char auther[20];//作者
	int num_pages;//页数

	char design[30];//设计
	char colors;//颜色
	int sizes;//尺寸
}book, cap, shirt;//创建结构体类型变量

typedef struct 
{
	int stock_number;//库存量
	double price;//定价
	int item_type;//商品类型

	char title[20];//书名
	char auther[20];//作者
	int num_pages;//页数
	
	char design[30];//设计
	char colors;//颜色
	int sizes;//尺寸
}gift_list;//创建结构体类型变量

 
int main()
{
	gift_list book, cap, shirt;

	return 0;
}

这是只使用struct 的情况,这样会浪费空间,为了使空间相对节省,我们可以使用联合体对代码加以改进

struct gift_list
{
	int stock_number;//库存量
	double price;//定价
	int item_type;//商品类型
	union Gift_list
	{
		struct
		{
			char title[20];//书名
			char auther[20];//作者
			int num_pages;//页数
		}book;
		struct
		{
			char design[30];//设计
		}cap;
		struct
		{
			char design[30];//设计
			char colors;//颜色
			int sizes;//尺寸
		}shirt;
	}item;
//使用联合体,共用一块空间,相对节省空间;其次可以使在不同的的时间下,调用同一块空间中的不同内容
};

联合体的练习

下面再看个关于联合体的练习:
写⼀个程序,判断当前机器是⼤端?还是⼩端?
代码如下:

//使用联合体进行大小端的判断

int main()
{
	int i = 1;
	if (*(char*)&i == 1)
	{
		printf("小端\n");
	}
	else 
	{
		printf("大端\n");
	}
	return 0;
}//这是之前学的,浅浅复习一下

union Un
{
	int i;//成员变量不可以初始化
	char a;
};
int main()
{
	union Un un = { 0 };
	un.i = 1;
	if (un.a == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}//这里是使用了联合体共用同一块空间,解决了判断大小端时需要第一个字节的内容
	return 0;
}

如果在同一时间使用一块空间,用结构体;如果在不同时间使用一块空间,用联合体。(我们也可以用买早餐和烧烤的可以使用同一块空间(也就是联合体)的案例进行记忆)

枚举

枚举就是将可能的值进行一一列举
比如

⼀周的星期⼀到星期⽇是有限的7天,可以⼀⼀列举
性别有:男、⼥、保密,也可以⼀⼀列举
⽉份有12个⽉,也可以⼀⼀列举
三原⾊,也是可以一一列举

枚举的关键字是enum
我们以性别为例进行枚举

enum Sex
{
	MALE,
    FEMALE,
	SECRET
};

我们可以将枚举进行打印观察下它的结果

enum Sex
{
	MALE,//这些都是常量,因此被叫做枚举常量
    FEMALE,
	SECRET
};

int main()
{
	enum Sex sex = MALE;//初始化枚举常量//直接使用枚举常量不初始化,可以正常使用,因为枚举常量本身就是编译期常量
	printf("%d\n", MALE);//0//这里是给枚举常量给一个初始值
	printf("%d\n", FEMALE);//1
	printf("%d\n", SECRET);//2
	return 0;
}

这些枚举常量都是有值的,它们从0开始,依次递增;如果在生命枚举类型的对其中一个进行赋值,那它就从赋值的那个开始依次递增,前面没赋值的从0开始依次递增,直到遇到赋值的
举例:

enum Day//星期
{
	Mon,
	Tues,
	Wed = 5,
	Thur,
	Fri,
	Sat= 10,
	Sun
};

int main()
{
	printf("%d\n", Mon);//0
	printf("%d\n", Tues);//1
	printf("%d\n", Wed);//5
	printf("%d\n", Thur);//6
	printf("%d\n", Fri);//7
	printf("%d\n", Sat);//10
	printf("%d\n", Sun);//11
	return 0;
}

枚举的优点

我们可以使⽤ #define 定义常量,为什么⾮要使⽤枚举?
枚举的优点:

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符⽐较枚举有类型检查,更加严谨。()
enum Sex
{
	MALE = 3,//0
	FEMALE = 5,
	SECRET = 7
};

//
int main()
{
	enum Sex sex1 = 3;
	//在C语言中,他不会报错,但是在C++中他会报错,这就是类型检查
	enum Sex sex2 = FEMALE;
	printf("%d\n", MALE);
	printf("%d\n", FEMALE);
	printf("%d\n", SECRET);

	return 0;
}
  1. 便于调试,预处理阶段会删除 #define 定义的符号
  2. 使⽤⽅便,⼀次可以定义多个常量
  3. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使⽤
    (#define没有作用域的概念,他从创建开始后一直可以使用)

枚举的应用

通过前面所学知识 — 设计一个计算器
代码如下:

void menu()
{
	printf("*****************************\n");
	printf("*****1.ADD		2.SUB	*****\n");
	printf("*****3.MUL		4.DIV	*****\n");
	printf("*****0.EXIT				*****\n");
	printf("*****************************\n");
	
}
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			break;
		case 2:
			break;
		case 3:
			break;
		case 4:
			break;
		case 0:
			printf("退出\n");
			break;
		default:
			printf("选择错误, 请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

这里的case 中的1、2、3、4的可读性较差,这里我们可以使用枚举增强代码的可读性
代码如下:

enum
{
	EXIT,
	ADD,
	SUB,
	MUL,
	DIV
};
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			break;
		case SUB:
			break;
		case MUL:
			break;
		case DIV:
			break;
		case EXIT:
			printf("退出\n");
			break;
		default:
			printf("选择错误, 请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值