C语言自定义类型变量——联合体(union)

前言:在之前的文章中我们介绍了C语言中自定义类型变量之一的结构体,本篇我们将介绍它的兄弟姐妹之一的联合体类型,何为联合体?如何声明?有什么特点?让我们一起揭开它神秘的面纱

一.何为联合体 ?

1.1联合体

我们知道,创建变量的过程本质上是在内存中为变量开辟一片内存空间的过程,每个变量都对应着相应的内存空间,各居其所,那么你是否试想过,多个不同变量是否能在同一块内存空间中先后存储呢?答案:yes。

这便需要引入我们今天介绍的联合体。像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以是不同类型。但不同的是,编译器只为联合体最大的成员分配足够的内存中间。联合体的特点是所有成员共用同一块内存空间 。因此,联合体又称为共用体。关键字union。

定义一个联合体的基本形式如下:

union 联合体名

{

//成员变量;

};

1.2联合体类型的声明和定义

同结构体类似,联合体也与结构体具有类似的声明和定义方式,具体如下:

cpp 复制代码
//一.同时创建模板和变量
union perdata
{
	char c;
	int i;
}un;

//二.先定义模板,再定义变量
union perdata
{
	char c;
	int i;
};

union perdata un1, un2;

//三.匿名联合体(一次性的,后面不能再次使用它定义新变量)
union
{
	char c;
	int i;
}un1, un2;

//使用typedef
typedef union perdata
{
	char c;
	int i;
}U_data;

U_data un1, un2;

1.3联合体的初始化

联合体初始化也类似于结构体,大致有以下三种方式:

一.将一个联合体初始化为另一个同类型联合;

二.一般初始化方式(仅初始化联合体第一个变量);

三.根据C99标准指定初始化方式。

cpp 复制代码
union perdata
{
	int num;
	char name;
}a,b;

int main()
{
	//一.将一个联合体初始化为另一个同类型联合
	a.num = 10;
	union perdata b = a;
	printf("%d\n", b.num);
	//二.一般初始化方式(仅初始化联合体第一个变量)
	union perdata b = { 10 };
	printf("%d\n", b.num);
	//三.指定初始化方式
	union perdata b = {.num=10};
	printf("%d\n", b.num);
    return 0;
}

但是由于联合体自身特点,若是大家初始化的时候不小心,就会存在一下情况:

cpp 复制代码
union perdata
{
	int num;
	char name;
}a,b;

int main()
{
	
	a.num = 10;
	a.name = 'c';
	union perdata b = a;
	printf("%d\n%c", b.num,b.name);//结果如何呢?
	return 0;
}

这个代码结果是否会如我们所想的10和c呢?

结果显示联合体b中的num并没有如我们所想的初始化为10,这是为什么呢?这便与联合体成员变量共用同一片内存空间的特点息息相关。

给联合体其中一个成员赋值,其他成员的值也跟着变化!

二.联合体的特点

在介绍联合体特点之前,我们先计算以下这个联合体的大小:

为什么是4呢?

2.1联合体的共用特性

联合的成员是共用同⼀块内存空间的,这样⼀个联合变量的大小,⾄少是最⼤成员的大小(因为联合至少得有能⼒保存最⼤的那个成员)。

如何证明联合体的成员是共用同一块内存空间的呢?

eg1:

cpp 复制代码
union perdata
{
	char c;
	int i;
};

int main()
{
	union perdata un = { 0 };
	//下面输出结果是否一致?
	printf("%p\n", &(un.i));
	printf("%p\n", &(un.c));
	printf("%p\n", &un);
	return 0;
}

结果:

0000007A68F4F744

0000007A68F4F744

0000007A68F4F744

说明联合体存储变量的时候都是从一个相同地址处开始存储的。

eg2:

cpp 复制代码
union perdata
{
	char c;
	int i;
};

int main()
{
	union perdata un = { 0 };
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);
	return 0;
}

结果:

11223355

为什么会是11223355呢?这既与小端存储有关,也与联合体的共用特性有关。

如图可以直观地观察到联合体中内存是如何存储以及修改变量的。这里也要注意,联合体覆盖变量时从低地址处开始覆盖。

2.2联合体与结构体相同成员对比

我们会有疑问,已经有了结构体变量,为什么还要引入联合体呢?正如一句话:存在即合理。联合体的出现必然是它存在一些异于结构体的优势,例如空间占用少。如图:

2.3联合体大小计算

在了解了联合体的特点之后,我们来看看联合体大小是如何计算的,我们已经知道了联合体各个成员公用同一块内存空间并且⼀个联合变量的大小,⾄少是最⼤成员的大小,那么许多人单纯地将联合体大小认为是最大成员变量的大小。事实并非如此。

1.联合的⼤⼩⾄少是最⼤成员的⼤⼩。

2. 当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。

cpp 复制代码
union Un
{
	char c[5];//1 8 1
	//5个char,char对齐数为1,整体对齐数只与变量类型有关而与个数无关 
	int i;//4 8 4
};

int main()
{
	printf("%zd\n", sizeof(union Un));//8
	return 0;
}

最大成员变量大小为5,但根据最大对齐数4,联合体空间需对齐到最大对齐数整数倍,所以要对齐到8,即联合体大小为8。

还不了解对齐数相关问题同学请移步C语言自定义类型变量------结构体(一)

三.联合体的应用

联合体基本介绍已经结束,接下来我们探讨一下联合体的实际应用:

3.1应用1

我们知道联合体是可以节省空间的,举例:⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。 每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。

假设我们没有学习过联合体,使用结构体将如何书写呢?

cpp 复制代码
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。

所以我们就可以把公共属性单独写出来,剩余属于各种商品本⾝的属性使⽤联合体起来,这样就可以 介绍所需的内存空间,⼀定程度上节省了内存。

结构如下:

cpp 复制代码
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;
};

3.2应用2

eg:写一个程序,判断当前机器的大小端?

cpp 复制代码
int check_sys()
{
	union
	{
		int i;
		char c;
	}un;
	un.i = 1;
	return un.c;
}
int main()
{
	printf("%d\n", check_sys());
	//结果为1则为小端
	//结果为0则是大端
	return 0;
}

可知本机器为小端存储。

以上便是对C语言中联合体有关内容介绍,望屏幕前的你能有所收获。

相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨10 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大12 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜12 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa13 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio