C-自定义类型-联合和枚举

时间:2024-10-24 19:17:18

1、联合体(union)

1.1 什么是联合体

联合体是一种特殊的数据类型,它类似于结构体,联合体也是由一个或者多个成员构成这些成员可以是不同的类型。

但是在联合体中,编译器只为最大的成员分配足够的内存空间。这使得联合体与结构体有一个明显的区别,联合体中所有成员共用同一块内存空间。所以联合体也叫:共用体

1.2 联合体的特点

因为联合体的这个特点,我们可以在某些场景下使用联合体节省空间,下面来看一下例子:
 

#include <stdio.h>
struct S//结构体
{
	char ch;//0
    //1 2 3
	int i;//4 8 4
    //4 5 6 7 
};
union U//联合体
{
	char ch;
	int i;
};
int main()
{
	printf("S=%d\n", sizeof(struct S));//打印结构体所占字节数
	printf("U=%d\n", sizeof(union U));//打印联合体所占字节数
	return 0;
}

 不知道大家是否还记得结构体中大小是怎么计算的了,这里也复习一下

首先字符类型ch从0开始偏移,整型类型 i自身大小是4,与默认对齐数8比较,i对齐数为4,因此1,2,3内存会被浪费,从4开始,4,5,6,7用来存储i的大小,因此总偏移量从0~7,占8个字节。所以结构体S的大小为8。

 联合体中,ch占一个字节,i占4个字节,结果打印总共占4个字节。

是不是很神奇,为什么联合体只占了4个字节的空间呢?

其实前面在介绍联合体的时候就已经说过了,在联合体中,编译器只为最大的成员分配足够的内存空间。在U中,字符类型的ch占1个字节,整型类型的 i 占4个字节,所以编译器只会为整型i分配4个内存的空间。

到这里我们大致清楚联合体的这个特点有什么作用。但是联合体究竟是怎么共用同一块空间的呢?

union U
{
	char ch;
	int i;
};
int main()
{
	printf("U=%d\n", sizeof(union U));
	union U u;
	printf("%p\n", &u);
	printf("%p\n", &(u.ch));
	printf("%p\n", &(u.i));
	return 0;
}

上图结果中我们可以看到无论是u还是u中的ch,u中的i,它们的地址都是一样的。

我们还可以换一种方式验证一下联合体中的成员都共用同一块空间。

union U
{
	char ch;
	int i;
};
int main()
{
	union U u;
	u.i = 0x11223344;
	u.ch = 0;
	return 0;
}

这里给 i 和 ch 分别赋值,我们来监视一下

先F10调试,打开内存监视窗口

找u的地址,然后开始调试 

 这里将 i 的值放到内存中了,i已经将4个字节占满了,接下来继续调试,

 可以发现第一个字节被改为了ch的值。

 也就是说,在联合体中,如果我们改变其中一个成员赋值,其它成员的值也会跟着变化。

要注意不要把结构体的思想带到联合体中,在联合体中,如果你打算使用ch的值,就不要使用i,使用i的时候就不要使用ch。一次只使用联合体中的一个成员。

1.3 联合体的使用场景

需要创建多个成员,但是这些成员又不会被同时使用,且想要节省空间,就可以使用联合体来办。

如果每个成员都需要被赋值,需要同时使用,那就不能创建为联合体。

1.4 联合体大小的计算

关于联合体大小,只需要遵循两点即可

  1. 首先找到最大成员的大小,如果是最大对齐数的整数倍,那么最大成员的大小就是联合体的大小
  2. 如果最大成员的大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

联合体的大小至少是最大成员的大小。

举个例子:

union U
{
	char ch[5];//5
	int i;//4
};
int main()
{
	printf("%zd\n", sizeof(union U));
	return 0;
}

上例中,联合体中最大成员的大小是5个字节,vs中默认最大对齐数是8,5不是8的整数倍,因此最终大小为8.

 

2、枚举(enum)

2.1 枚举类型的声明

说到枚举,想必大家都不觉得陌生,相比起联合体,枚举这个名词我们在很多地方都听说过,它在数学中又名穷举法。

还是简单介绍一下吧!

枚举顾名思义就是一一列举。

把可能的取值一一列举。

在现实生活中又一些枚举例子:

  • 一周的星期一到星期日是有限的7天,可以一一列举
  • 一年的月份有12个月,也可以一一列举
  • 性别有男,女,保密,也可以一一列举

向这样的数据就可以使用枚举了。

那么计算机中的枚举该怎么表示呢?

enum Sex
{
   //枚举类型的可能取值
	men,//常量
	women,
	secret
};

上面enum是枚举关键字,Sex是枚举类型的名称,大括号中的便是枚举的成员,也都是常量,它们也被称为枚举常量。常量与常量之间用','隔开。

那么这些枚举常量的可能取值是多少呢?

#include <stdio.h>
enum Sex
{
	men,
	women,
	secret
};
int main()
{
	printf("%d\n", men);
	printf("%d\n", women);
	printf("%d\n", secret);
	return 0;
}

打印结果:

从这里我们可以看到枚举常量的可能取值是从0开始向下递增的,如果还有第四个常量,便会出现3……

枚举中它们为什么是常量呢?因为它们的值不能修改。如果修改它们的值会怎么样?

可以看到直接报错,不能修改。

虽然不能修改,但是我们可以给它们赋初始值,比如说

enum Sex
{
	men=1,
	women=4,
	secret=8
};

如果我们只给第一个常量赋初始值,会怎么样呢?

 它会沿着第一个值向后递增。

如果我们不给第一个赋值,给第二个赋值,会怎么样呢?

可以看到第一个值还是默认的0,但是第三个值会沿着第二个赋的值继续向后递增。 

2.2 枚举类型的优点 

我们可以使用#define定义常量,为什么非要使用枚举呢?这不是多次一举吗?

但是事实并非如此,枚举还是有很多好处的。

枚举的优点:

  • 增加代码的可读性和可维护性
  • 和#define定义的标识符相比枚举有类型检查,更加严谨
  • 使用方便,一次可以定义多个常量
  • 枚举常量遵循作用域规则的,枚举声明在函数内,只能在函数内使用

相关文章