C语言王国——深入自定义类型(联合体、枚举)

2024-06-21 08:36

本文主要是介绍C语言王国——深入自定义类型(联合体、枚举),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、引言

二、联合体

2.1 联合体类型的声明

2.2 联合体大小的计算

2.3 联合体的实践运用

2.4 用联合体测试大小端字节序

三、枚举

3.1 枚举类型的声明

3.2 枚举类型的特点

四、总结


一、引言

我们刚学完了结构体,相信大家对自定义类型也有了些许了解,但是自定义类型中还有两个我们尚未学习,它们也急不可耐的想展现在我们面前了,所以接下来就由姜糖来带大家认识一下吧!


二、联合体

2.1 联合体类型的声明

像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。 但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所 以联合体也叫:共用体

所以联合体的声明和结构体类似,结构体的关键词是struct,而联合体的关键词是union。

union Un
{char a;int b;
};

2.2 联合体大小的计算

接下来我们再来探讨一下联合体的存储大小,我们用代码测试一下上面定义的联合体:

#include<stdio.h>union Un
{char a;int b;
}un;int main()
{printf("%zd", sizeof(un));return 0;
}

打印结果为:

 而如果此为结构体,考考大家前面学过的知识,应该为多少呢?        ·(答案为8)

那么我们发现,联合体和结构体的存储模式是不一样的。那么联合体是怎么存储的呢?

联合体的存储规则:

  • 联合体的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。 

首先我们来了解第一句话, 正如我们前面讲到的那样,联合体的特点是所有成员共用同⼀块内存空间,所以联合体大小至少是最大成员的大小,不然当我们用到最大成员时,联合体就存储不下了。

那么接下来我们用代码看看联合体的成员是否是共用一块内存空间:

#include<stdio.h>union Un
{char a;int b;
}un;int main()
{printf("%p\n", &un);    //联合体的地址printf("%p\n", &(un.a));//联合体中a成员的地址printf("%p\n", &(un.b));//联合体中b成员的地址return 0;
}

打印结果如下:

我们发现它们的地址是一样的,我们可以确认它们都为应该地址,为了更加确定这个观点,我们来进一步测试:

#include<stdio.h>union Un
{char a;int b;
}un;int main()
{un.b = 0x11223344;un.a = 0x66;printf("%x", un.b);//看看a的改变是否影响breturn 0;
}

打印结果如下:

 所以可以更一步的确定它们共用一块内存,所以这个联合体的图为:

所以我们发现联合体比结构体更节约存储空间。

而第二句话我们该怎么理解呢?

我们用代码具体分析,比如:

union Un
{char a[6];int b;
}un;int main()
{printf("%zd", sizeof(un));return 0;
}

那么之里面的最大成员为char a[6],那么大小就为6了吗,真的是这样子的吗?那我们来看打印结果:

结果不为6而为8 ,而这里和结构体一样要对齐

所以最后6应该变成4的倍数8。 

我们也发现,其实联合体深入节约存储空间但也会存在内存浪费的现象。

2.3 联合体的实践运用

那么我们学习了联合体,那联合体有什么实际运用呢?

大家在前面的学习中也发现了联合体的节约空间的性质,那么实际运用中也和节约空间密不可分,比如生活中常见的事务的分类,那我们拿书、手机、手表来举例。

首先书、手机、手表都是一个大类——售卖的商品

手机:价格、品牌

书:价格、作者、书名

手表:价格、品牌、工艺

那么它们的结构体该怎么写呢?

struct commodity
{//公共属性int price;	//价格//私有属性char brand[10];//品牌char author[10];//作者char title[10];//书名char workmanship[10];//工艺
};

我们发现这里有很多私有属性,会被大量浪费。当这些私有属性共用一块内存空间时是不是可以大大节约空间的浪费所以接下来,我们用union联合体来优化代码:

struct commodity
{int price;	//价格union{struct{char brand[10];//品牌}phone;struct{char author[10];//作者char title[10];//书名}book;struct{char workmanship[10];//工艺char brand[10];//品牌}Wristwatch;}item;
};

这样设计结构体就能大大减少空间的浪费。 

2.4 用联合体测试大小端字节序

在学习的过程中我们也发现联合体非常适合去测试大小端的字节序,因为前面我们在测试大小端的时候我们是将同一个内存强制类型转换的,而这里我们用联合体将两个类型保存在同一个空间中就行了,代码如下:

#include<stdio.h>union Un
{int a;char b;
};int main()
{union Un un = { 0 };un.a = 1;if (un.b == 1){printf("为小端");}elseprintf("为大端");return 0;
}


三、枚举

3.1 枚举类型的声明

枚举顾名思义就是⼀⼀列举。 把可能的取值⼀⼀列举。

而我们生活中有很多可以一一列举的事务,如星期,月份,三原色等等,比如三原色的枚举: 

emun Threeprimarycolors
{red;blue;green;
};

{}中的内容是枚举类型的可能取值,也叫 枚举常量。这些可能取值都是有值的,默认从0开始,依次递增1:

比如这里的red就为0,blue就为1green就为2

当然在声明枚举类型的时候也可以赋初值:

emun Threeprimarycolors
{red;blue = 6;green;
};

这样就blue就变成了6,前面没有赋值的就按照原来的从0开始,red0,赋值后面的则继续加一,green就为7

3.2 枚举类型的特点

我们发现枚举和#define定义非常相像,那么我们可以使用 #define 定义常量,为什么非要使用枚举?

枚举的优点:

1. 增加代码的可读性和可维护性。

2. 和#define定义的标识符比较枚举有类型检查,更加严谨。

3. 便于调试,预处理阶段会删除 #define 定义的符号。

4. 使用方便,⼀次可以定义多个常量

5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用。


四、总结

C语言的内容不止于此,还有更多需要我们共同发现,今天C语言的内存数据管理就到这里啦。如果姜糖有讲的不好的地方欢迎大家提出,谢谢大家,也希望大家能一键三连哦!


这篇关于C语言王国——深入自定义类型(联合体、枚举)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1080728

相关文章

SpringBoot全局异常拦截与自定义错误页面实现过程解读

《SpringBoot全局异常拦截与自定义错误页面实现过程解读》本文介绍了SpringBoot中全局异常拦截与自定义错误页面的实现方法,包括异常的分类、SpringBoot默认异常处理机制、全局异常拦... 目录一、引言二、Spring Boot异常处理基础2.1 异常的分类2.2 Spring Boot默

自定义注解SpringBoot防重复提交AOP方法详解

《自定义注解SpringBoot防重复提交AOP方法详解》该文章描述了一个防止重复提交的流程,通过HttpServletRequest对象获取请求信息,生成唯一标识,使用Redis分布式锁判断请求是否... 目录防重复提交流程引入依赖properties配置自定义注解切面Redis工具类controller

MyBatis中的两种参数传递类型详解(示例代码)

《MyBatis中的两种参数传递类型详解(示例代码)》文章介绍了MyBatis中传递多个参数的两种方式,使用Map和使用@Param注解或封装POJO,Map方式适用于动态、不固定的参数,但可读性和安... 目录✅ android方式一:使用Map<String, Object>✅ 方式二:使用@Param

C语言逗号运算符和逗号表达式的使用小结

《C语言逗号运算符和逗号表达式的使用小结》本文详细介绍了C语言中的逗号运算符和逗号表达式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习... 在C语言中逗号“,”也是一种运算符,称为逗号运算符。 其功能是把两个表达式连接其一般形式为:表达

Go语言实现桥接模式

《Go语言实现桥接模式》桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立地变化,本文就来介绍一下了Go语言实现桥接模式,感兴趣的可以了解一下... 目录简介核心概念为什么使用桥接模式?应用场景案例分析步骤一:定义实现接口步骤二:创建具体实现类步骤三:定义抽象类步骤四:创建扩展抽象类步

GO语言实现串口简单通讯

《GO语言实现串口简单通讯》本文分享了使用Go语言进行串口通讯的实践过程,详细介绍了串口配置、数据发送与接收的代码实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 目录背景串口通讯代码代码块分解解析完整代码运行结果背景最近再学习 go 语言,在某宝用5块钱买了个

C# WebAPI的几种返回类型方式

《C#WebAPI的几种返回类型方式》本文主要介绍了C#WebAPI的几种返回类型方式,包括直接返回指定类型、返回IActionResult实例和返回ActionResult,文中通过示例代码介绍的... 目录创建 Controller 和 Model 类在 Action 中返回 指定类型在 Action

kafka自定义分区器使用详解

《kafka自定义分区器使用详解》本文介绍了如何根据企业需求自定义Kafka分区器,只需实现Partitioner接口并重写partition()方法,示例中,包含cuihaida的数据发送到0号分区... 目录kafka自定义分区器假设现在有一个需求使用分区器的方法总结kafka自定义分区器根据企业需求

GO语言zap日志库理解和使用方法示例

《GO语言zap日志库理解和使用方法示例》Zap是一个高性能、结构化日志库,专为Go语言设计,它由Uber开源,并且在Go社区中非常受欢迎,:本文主要介绍GO语言zap日志库理解和使用方法的相关资... 目录1. zap日志库介绍2.安装zap库3.配置日志记录器3.1 Logger3.2 Sugared

Go语言中如何进行数据库查询操作

《Go语言中如何进行数据库查询操作》在Go语言中,与数据库交互通常通过使用数据库驱动来实现,Go语言支持多种数据库,如MySQL、PostgreSQL、SQLite等,每种数据库都有其对应的官方或第三... 查询函数QueryRow和Query详细对比特性QueryRowQuery返回值数量1个:*sql