C语言自定义类型——联合体、枚举

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:


提示:以下是本篇文章正文内容,下面案例可供参考
本文主要介绍了C语言中自定义模型的剩下两种类型——联合体和枚举。

一、联合体

(一)、联合体的声明

  • 联合体的关键字为union,它的声明跟结构体类型,联合体也是由一个或者多个成员构成,这些成员可以选择不同的类型。但是编译器只为最大的成员分配足够的空间。因为联合体所有的成员共用一块内存空间,所有联合体也叫做:共用体
  • 下面是联合体的一段声明和定义变量的代码:
#include<stdio.h>
union Un
{
char c;
int i;
};
int main()
{
//联合体变量的定义,并初始化
union Un un={0};

}

(二)、联合体的特点

  • 联合体最大的特点就是联合体的成员共用一块内存空间,这样一个联合变量的大小,至少是最大成员的大小(因为联合体至少得有能力保存最大的那个成员)
    以代码来验证咱们上面的观点:
#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;
}

运行结果如下:
在这里插入图片描述
从上面运行结果我们可以看出:联合体的成员确实共用一块内存空间,因为它们的起始地址都是一样的。

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

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

运行结果如下:
在这里插入图片描述
我们可以看出我们通过改变结构体成员c的值间接性改变了成员i的值(从原来的0x11223344->0x11223355)

(三)、联合体大小的计算!!

  • 要计算联合体的大小我们要了解以下两条规则:
    联合体的大小至少是最大成员的大小;
    当最大成员的大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍;
  • 我们分析一下下面代码中联合体的大小:
#include <stdio.h>
 union Un1
 {
 char c[5];
 int i;
 };
 union Un2
 {
 short c[7];
 int i;
 };
 int main()
 {
 printf("%d\n", sizeof(union Un1));
 printf("%d\n", sizeof(union Un2));
 return 0;
 }

运行结果如下:
在这里插入图片描述
我们从上面特点知道,联合体的大小至少是最大成员的大小,且必须是成员最大对齐数的整数倍。对于结构体Un1来说,最大成员为c,它的大小是5个字节,而最大对齐数是i的对齐数为4,故而应为4的倍数,所以我们应该浪费3个字节,最终Un1结构体的大小为8个字节;对于结构体Un2来说,最大成员为c,它的大小为14个字节,而最大对齐数是i的对齐数为4,故而应为4的倍数,所以我们应该浪费2个字节,最终Un2结构体的大小为8个字节。

(四)、联合体的小应用

  • 使用联合体可以节省空间:
    咱们以下面一个例子来说明:
    比如,我们要搞一个活动,要上线一个礼品兑换单,礼品兑换单中有三种商品:图书、杯子、衬衫。
    每种商品都有:库存量、价格、商品类型和商品类型相关的其他信息;例如图书还有的属性:书名、作者、页数;杯子还有的属性:设计;衬衫还有的属性:设计、可选颜色、可选尺寸。
    如果我们不考虑很多的话,可以直接粗暴地定义结构体直接将所有属性囊括即可:
struct gift_list
{
//公共地属性
int stock_number;//库存量
double price;//定价
int item_type;//商品类型

//特殊属性:
char title[20];//书名
char author[20];//作者
int num_pages;//页数


int 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
{
int design[30];//设计
}mug;//杯子的特殊属性
struct
{
int design[30];//设计
int colors;//颜色
int sizes;//尺寸
}shirt;//衬衫的特殊属性
}item;//特殊属性
};
  • 联合体可以用来判断机器小大端字节序。
    前面我们写的判断大小端字节序的代码为:
#include<stdio.h>
int main()
{
	int a = 1;
	//0x 00 00 00 01
	if (*((char*)&a) == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
}

而我们可以充分利用联合体的内存分配特点,联合体的成员共有一块内存空间,修改代码如下:

#include<stdio.h>
union Un
{
	char c;
	int i;
};
int main()
{
	union Un un = { 0 };
	un.i = 1;
	if (un.c == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
}


这样会使得代码更加简单好理解,少去了取地址和强转类型的操作。

二、枚举

(一)、枚举的类型声明

  • 我们都知道枚举顾名思义就是一 一地列举,把可能地值一一地给列出来
  • 枚举地关键字是enum,我们以星期天为例来进行枚举地声明
#include<stdio.h>
enum Day//星期
{
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
}D;
int main()
{
	printf("%d\n",Mon);
	printf("%d\n",Tues);
	printf("%d\n",Wed);

}

运行结果如下:
在这里插入图片描述
枚举中成员的取值,默认从1开始,依次递增1,当然也可以手动赋值。
以颜色为例:

#include<stdio.h>
enum
{
RED=2,
GREEN=4,
BLUE=8
};
int main()
{
printf("%d\n",RED);
printf("%d\n",GREEN);
printf("%d\n",BLUE);
}

运行结果如下:
在这里插入图片描述

(二)、枚举的类型优点

我们都知道,定义常量的话可以用#define来实现,那我们用枚举的优点是什么呢?

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

(三)、枚举类型的使用

  • 咱们以构造一个计算器为例来阐述一下枚举的使用
    原先我们设置的计算器为:
void meau()
{
	printf("*****************************\n");
	printf("****1.ADD      2.SUB   ******\n");
	printf("****3.MUL      4.DIV   ******\n");
	printf("****0.Exit             ******\n");
	printf("*****************************\n");
	printf("*****************************\n");

}
int aDD(int x, int y)
{
	return x + y;
}
int sUB(int x, int y)
{
	return x - y;
}
int mUL(int x, int y)
{
	return x * y;
}
int dIV(int x, int y)
{
	return x / y;
}
int main()
{

	int input=0;
	int a = 0, b = 0;

	
	do {
		int result = 0;
		meau();
		printf("请选择->:\n");
		scanf("%d", &input);
		printf("请输入要进行操作的两个数:\n");
		scanf("%d %d", &a, &b);
		switch (input)
		{
		case 1:
			result = aDD(a, b);
			printf("加法的运算结果为:%d\n", result);
			break;

		case 2:
			result = sUB(a, b);
			printf("减法的运算结果为:%d\n", result);
			break;
		case 3:
			result = mUL(a, b);
			printf("乘法的运算结果为:%d\n", result);
			break;

		case 4:
			result = dIV(a, b);
			printf("除法的运算结果为:%d\n", result);
			break;
		case 0:
			printf("退出运算\n");
			exit(-1);
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while(input);
}

我们在switch中还是用数字0,1,2,3,4来做选择,不太直观。
而利用上枚举类型后我们可以修改代码:

#include<stdio.h>
void meau()
{
	printf("*****************************\n");
	printf("****1.ADD      2.SUB   ******\n");
	printf("****3.MUL      4.DIV   ******\n");
	printf("****0.Exit             ******\n");
	printf("*****************************\n");
	printf("*****************************\n");

}
enum Option//更加直观
{
	Exit,
	ADD,
	SUB,
	MUL,
	DIV
};

int aDD(int x, int y)
{
	return x + y;
}
int sUB(int x, int y)
{
	return x - y;
}
int mUL(int x, int y)
{
	return x * y;
}
int dIV(int x, int y)
{
	return x / y;
}
int main()
{

	int input=0;
	int a = 0, b = 0;

	
	do {
		int result = 0;
		meau();
		printf("请选择->:\n");
		scanf("%d", &input);
		printf("请输入要进行操作的两个数:\n");
		scanf("%d %d", &a, &b);
		switch (input)
		{
		case ADD:
			result = aDD(a, b);
			printf("加法的运算结果为:%d\n", result);
			break;

		case SUB:
			result = sUB(a, b);
			printf("减法的运算结果为:%d\n", result);
			break;
		case MUL:
			result = mUL(a, b);
			printf("乘法的运算结果为:%d\n", result);
			break;

		case DIV:
			result = dIV(a, b);
			printf("除法的运算结果为:%d\n", result);
			break;
		case Exit:
			printf("退出运算\n");
			exit(-1);
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while(input);
	



}

运行结果如下:
在这里插入图片描述

我们在代码中充分利用了枚举类型,这样增加代码的可读性与维护性,会让使用者更加直观、清晰明了,这就是枚举的重要使用作用。

总结

本文主要介绍了C语言中自定义类型的剩余两种——联合体和枚举类型,如有错误,请批评指正,感谢支持

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 02:56:02       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 02:56:02       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 02:56:02       57 阅读
  4. Python语言-面向对象

    2024-07-10 02:56:02       68 阅读

热门阅读

  1. UniVue@v1.2.0版本发布

    2024-07-10 02:56:02       25 阅读
  2. 【Lua】元表使用示例

    2024-07-10 02:56:02       26 阅读
  3. 使用 apktool 解包 apk 并重新打包签名

    2024-07-10 02:56:02       21 阅读
  4. Mobile ALOHA前传之VINN, Diffusion Policy和ACT对比

    2024-07-10 02:56:02       24 阅读
  5. React面试题之setState的执行机制

    2024-07-10 02:56:02       23 阅读
  6. 如何控制代码质量

    2024-07-10 02:56:02       22 阅读
  7. C++常用类

    2024-07-10 02:56:02       26 阅读
  8. springboot 与 ipv6

    2024-07-10 02:56:02       21 阅读
  9. UI还原度小技巧之缩放

    2024-07-10 02:56:02       27 阅读
  10. 腾讯centos mysql安装

    2024-07-10 02:56:02       21 阅读