自定义类型:联合和枚举

目录

联合体(共用体)

联合体类型的声明和定义

相同成员的结构体和联合体的对比

联合体判断大小端

联合体大小的计算

枚举

什么是枚举

枚举类型的定义

枚举常量

枚举类型的优点


联合体(共用体)

联合体类型的声明和定义

像结构体⼀样, 联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。
但是编译器只为 最大的成员分配足够够的内存空间。联合体的特点是所有成员共用同⼀块内存空间。 所以联合体也叫:共用体
给联合体其中⼀个成员赋值,其他成员的值也跟着变化。
大家猜猜这个联合体变量un的大小
union Un
{
	char c;
	int i;
};

int main()
{
	//联合变量的定义
	union Un un = { 0 };
	//计算联合变量的大小
	printf("%d\n", sizeof(un));
	return 0;
}

 运行结果:

为什么是4个字节呢?

首先我们前面已经说过了:联合体的成员是共用同一块空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)


这个联合体有2个成员:char c 和 int i,c自身大小是1,i自身大小是4,因为它们共用同一块空间,并且这个联合体中最大默认对齐数也是4,所以分配四个字节就够了。
4个字节既可以放得下c ,也能放得下i

因为我们前面说过联合体的成员是共用同一块空间的,这要这么证明?

union Un
{
	char c;
	int i;
};

int main()
{
	//联合变量的定义
	union Un un = { 0 };
	//判断下面输出的结果是一样的吗?
	printf("%p\n", &(un.c));
	printf("%p\n", &(un.i));
}

因为它们共用同一块空间,所以修改任何一个成员变量的值都有可能会改变另一个成员变量
union Un
{
	char c;
	int i;
};

int main()
{
	//联合变量的定义
	union Un un = {0};
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);
}

运行结果:

相同成员的结构体和联合体的对比

我们再对比一下相同成员的结构体和联合体的内存布局情况。

联合体判断大小端

我相信如果我们没有学过联合体,我们来判断大小端肯定会使用指针来做
int main()
{
	int n = 1;
	int ret = *(char*)&n;
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
}

当然啦,除了用指针的方式来做,我们还可以使用联合体来判断

union Un
{
	char c;
	int i;
};

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

联合体大小的计算

我们在上面举的例子中的联合体大小就是它的最大成员的大小,但是不是所有的联合体大小都是这样呢?
不是的,联合体大小的计算其实也要考虑对齐的。

  • 联合的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
union Un1
{
	char c[5];
	int i;
};

union Un2
{
	short c[7];
	int i;
};

int main()
{
	//下面输出的结果是什么?
	printf("%zd\n", sizeof(union Un1));
	printf("%zd\n", sizeof(union Un2));
	return 0;
}

运行结果:

联合体union Un1的的分析

1.首先第一个成员变量c是数组,占5字节

2.第二个成员变量i占4字

判断对齐数:

1.如果是数组的话,是按数组一个元素的大小来和默认对齐数来进行比较的,取它们中的最小值。因为数组c元素类型是char,占1字节,而默认对齐数是8,所以它的对齐数是1

2.i是int类型的,所以占4字节,而默认对齐数是8,所以它的对齐数是4

3.因为这个联合体成员变量中最大默认对齐数是4,而现在联合体所占大小是5字节,5不是4的倍数,所以还要向后占用3个字节的内存空间


联合体union Un2的的分析

1.首先第一个成员变量c是数组,占14字节

2.第二个成员变量i占4字

判断对齐数:

1.如果是数组的话,是按数组一个元素的大小来和默认对齐数来进行比较的,取它们中的最小值。因为数组c元素类型是short,占2字节,而默认对齐数是8,所以它的对齐数是2

2.i是int类型的,所以占4字节,而默认对齐数是8,所以它的对齐数是4

3.因为这个联合体成员变量中最大默认对齐数是4,而现在联合体所占大小是14字节,14不是4的倍数,所以还要向后占用2个字节的内存空间


使用联合体是可以节省空间的,举例:
比如,我们要搞⼀个活动,要上线一个礼品兑换单,礼品兑换单中有三种商品:图书、杯子、衬衫。
每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、页数
杯子:设计
衬衫:设计、可选颜色、可选尺寸
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 un
	{
		//联合体中有三个成员变量
		//匿名结构体变量book和匿名结构体变量mug和匿名结构体变量shirt
		//它们三共用同一块空间
		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;
};

创建相对应的变量(比如S1代表图书,S2代表衬衫),因为我们都有共用的属性,并且也有部分商品本身的属性,所以在结构体中创建联合体把部分属性共用起来就可以节省空间

注意:这个结构体要保存不同商品的信息,但是创建变量时我们是明确知道它要表示什么,所以用联合体来搭配就很不错

枚举

什么是枚举

枚举顾名思义就是⼀⼀列举。
把可能的取值⼀⼀列举。
比如我们现实生活中:

一周的星期一到星期日是有限的7天,可以一一列举

性别有:男、女、保密,也可以一一列举

月份有12个月,也可以一一列举

三原色,也是可以一一列举

枚举类型的定义

enum Gender
{
	MALE,
	FEMALE,
	SECRET
};

使用枚举定义类型的注意事项:

枚举常量

enum Color
{
	//这里列举enum Color的可能取值
	RED,
	GREEN,
	BLUE
};

int main()
{
	printf("RED=%d\n", RED);
	printf("GREEN=%d\n", GREEN);
	printf("BLUE=%d\n", BLUE);
	return 0;
}

运行结果:

{}中的内容是枚举类型的可能取值,也叫枚举常量 。
这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。

这些枚举类型的可能取值也叫做枚举常量,那既然是常量,就意味这不能被修改

但是我们可以在定义的时候赋初始值

那如果只给第一个赋初值,就从该初值开始,还是依次增1。

枚举变量只能用枚举常量来赋值

enum Color
{
	//这里列举enum Color的可能取值
	RED = 1,
	GREEN = 2,
	BLUE = 3
};

int main()
{
	enum Color c1 = RED;
	printf("%d\n", c1);
	return 0;
}

但是有人说,我就用常量来赋值,不用枚举常量赋值程序也可以通过啊

原因:这里没有报错是因为C语言语言检查不严格,用CPP也就是c++去测就会报错

枚举类型的优点

为什么要用枚举?

  1. ​​增加代码的可读性和可维护性
  2.  和#define定义的标识符比较枚举有类型检查,更加严谨。
  3. 便于调试,预处理阶段会删除 #define 定义的符号
  4.  使用方便,一次可以定义多个常量 
  5. 枚举常量是遵循作业域规则的,枚举声明在函数内,只能在函数内使用

例子:

就像我们之前做的扫雷游戏

我们会做一个菜单,让用户在使用时根据菜单的提示做出相应的选择。

但是阅读者在看代码的时候,看到switch语句中case后面的值,可能还需要翻到上面看看它对应的是哪个功能。

但是,如果我们的程序已经完全写好之后,我们case语句对应的值,是不是就确定了
那这个时候,我们是不是就可以考虑用一个枚举呢?

  • 35
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值