在21世纪的我用C语言探寻世界本质——自定义类型:联合体和枚举

在这里插入图片描述

人无完人,持之以恒,方能见真我!!!
共同进步!!

一、联合体(共同体)

1.联合体类型的声明

像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以是不同的类型

联合体的特点是所有成员共⽤同⼀块内存空间,所以联合体也叫共同体,由于所有成员共用一块空间,所以编译器只为最⼤的成员分配足够的内存空间 ,并且当给联合体其中⼀个成员赋值时,其他成员的值也跟着变化,我们后面也会讲到

现在我们从联合体类型的声明开始学习,它的声明也和结构体的声明相似,结构体声明时使用struct关键字,而联合体声明时使用union关键字,如下:

union un
{
	char c;
	int i;
};

它创建变量的方式和结构体都是类似的,如下:

union un
{
	char c;
	int i;
};

int main()
{
	union un s;
	return 0;
}

联合体与结构体是差不多的,这里就不多赘述

2.联合体的特点

联合的成员是共用同⼀块内存空间的,这样⼀个联合变量的大小,至少是最⼤成员的大小

测试1

现在我们来做个测试,看一下联合体成员的地址是否相同,以及联合体本身的地址和它成员地址的关系

#include <stdio.h>

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;
}

运行结果:

在这里插入图片描述

可以看到,联合体成员的地址是相同的,并且联合体本身也和联合体成员的地址相同,这就可以说明联合体开辟空间的时候,所有的联合体成员公用一块空间

测试2

接下来我们再举一个例子来测试联合体的空间是否是共用的

#include <stdio.h>

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共用相同的空间,那么我们在更改c的时候,i应该也会跟着改变,我们来看看内存里的存储:

这是初始化完i时,结构体内存的存储:

在这里插入图片描述

可以看到,这里VS用小端字节序的方式将i存放到了内存中
现在已经把i存放进内存了,代码下一步就是将c改成16进制数55,那我们看看i会不会跟着一起改变:

在这里插入图片描述

可以看到内存中i的第一个字节被修改为了55,说明更改c确实连带着把我们的i更改了,我们可以画一个图来更清楚的阐述这个变化过程:

在这里插入图片描述

所以在这个联合体中,我们可以分析得到:整型变量i和字符变量c占据同一块空间,而c占据的就是i的第一个字节,当我们对c进行修改时,也就是对i第一个字节的修改,所以当我们把c修改为0x55时,i也就跟着改变了

运行结果:

在这里插入图片描述

通过这个例子再一次有利的证明的联合体成员共用一块空间

3.联合体大小的计算

联合体的存储规则

  1. 联合体的大小至少是最⼤成员的大小,确保联合体的大小可以装下每一个单一成员
  2. 当最⼤成员大小不是最大对齐数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍

话不多说,直接上例子

例1

#include <stdio.h>

union Un1
{
	char c[5];
	int i;
};

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

根据第一条规则我们知道了联合体 Un1 的大小至少是5个字节,根据第二条规则我们知道了Un1的大小必须是4的倍数,所以综和这两点,答案已经呼之欲出:结构体 Un1 的大小是8个字节

在这里插入图片描述

例2

#include <stdio.h>

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

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

首先根据第一条规则,我们要看成员中,谁最大,很明显最大的就是c数组,占据14个字节,所以联合体Un2至少都有14个字节

然后来看第二个规则,我们要看成员中的最大对齐数,第一个成员c的对齐数是2,第二个成员的对齐数是4,所以联合体 Un2 的最大对齐数是4,它的大小应该是4的倍数

所以综上,联合体 Un2 的大小至少14个字节,还要是4的倍数,所以联合体Un2的大小为16个字节

在这里插入图片描述

4.联合体小练习

使用联合体写⼀个程序,判断当前机器是大端字节序还是小端字节序

创建一个整型变量,赋值为1,然后将它的地址强制转换为字符类型存放起来,然后通过这个字符指针去访问整型变量的第一个字节,看看拿到的是否是1

如果是1说明是小端字节序,是0就是大端字节序,这里再放一下它的代码,如下:

#include <stdio.h>

int main()
{
	int a = 1;
	char* p = (char*)&a;
	if (*p == 1)
		printf("小端字节序\n");
	else
		printf("大端字节序\n");
	return 0;
}

在这里插入图片描述

接下来我们就来看如何使用联合体实现这个功能

只要在联合体中创建一个整型成员a,创建一个字符型成员c,由于共用空间,那么c就可以直接访问a的第一个字节

#include <stdio.h>

union Un
{
	int a;
	char c;
};

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

在这里插入图片描述

5.结构体和联合体内存占用的对比

学习了联合体和结构体之后,我们来简单对比一下同样的成员下,结构体和联合体内存占用的情况

struct S
{
 char c;
 int i;
};

union Un
{
 char c;
 int i;
};

在这里插入图片描述

可以看到,和结构体对比,联合体非常节省空间

6.联合体的应用

联合体在使用时可以节省空间,所以我们要学习什么情况下使用联合体,而不是使用结构体

由于它的特性,所以我们应该也能想到它的应用,那就是应用在整个联合体一次性只会出现一个成员的情况下,只要我们在同一时刻只使用一个成员,那么就算把其它成员改变了也没有影响

⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息,如下:

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

我们可以直接写出以下结构:

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.枚举类型的声明

枚举顾名思义就是⼀⼀列举,可以把所有可能的取值⼀⼀列举出来

  1. ⼀周的星期⼀到星期⽇是有限的7天,可以⼀⼀列举
  2. 性别有:男、⼥,也可以⼀⼀列举
  3. 三原⾊,也是可以一一列举
enum Day//星期
{
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};

enum gender//性别
{
    MALE,
    FEMALE,
};


enum Color//三原色
{
    RED,
    GREEN,
    BLUE
};

以上定义的 enum Day , enum Sex , enum Color 都是枚举类型,{}中的内容是枚举类型的可能取值,也叫 枚举常量

这些枚举常量都是有值的,默认从0开始,依次递增1,我们可以打印出来看看:

#include <stdio.h>

enum color
{
	RED,
	GREEN,
	BLUE
};

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

在这里插入图片描述

当然枚举类型也是可以赋值的

enum Color
{
	RED = 2,
	GREEN = 4,
	BLUE = 8
};

在这里插入图片描述

这就是自定义结构:枚举,里面的成员又叫枚举常量,是无法更改的,一般用来将这些值赋值给其它变量

2.枚举类型的优点

  1. 增加代码的可读性和可维护性,比如我们想用数字0表示男,数字1表示女,那么写出来就不好理解,如果用枚举中MALE表示男,同时底层代表0,就具有更高的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨
  3. 便于调试,预处理阶段会删除 #define 定义的符号,这个在后面的预处理详解我们会讲到
  4. 使⽤⽅便,⼀次可以定义多个常量
  5. 枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使用,而#define定义的常量是全局变量

3.枚举类型的使用

在使用枚举时,我们会创建一个枚举变量,然后用枚举类型中的枚举常量给它赋值

#include <stdio.h>

enum Color
{
	RED = 1,
	GREEN = 2,
	BLUE = 4
};

int main()
{
	enum Color clr = GREEN;
	return 0;
}

到这里我们联合体以及枚举的学习就已经完结了,使用方法与结构体相似,希望大家收获多多

评论 149
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值