「自定义类型」C语言中的构造数据类型如结构,联合,枚举

news2024/12/25 9:12:42
​​​​​​​🚀🚀🚀 大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

🐰结构

🏡 前言

🌸数据类型的定义

🌸关键字struct 与 class 的困惑

🌸使用struct

🐰位域

🏡前言

🤔提示

🤔提示

🌸成员对齐

🌸结构内存大小的计算

🐰联合(Union)

🏡前沿

🌸联合内存大小的计算

🐰枚举(Enum)​​​​​​​

🏡前言

🌸枚举类型的大小

🤔‼️提示


🐰结构

🏡 前言

如果只能使用基本数据类型来编程,那将是一件痛苦的事情。C语言支特把基本数据类型组合起来形成更大的构造数据类型,这就是C语言的struct,有时也称为用户自定义数据类型 (User defined Type, UDT)。

🌸数据类型的定义

构造数据类型还可以嵌套(对象嵌入)和引用(对象关联),实际上,构造数据类型是一个递归的定义:
(1)由若干基本数据类型组合而成的类型是构造数据类型
(2)由若干基本数据类型和构造数据类型组合而成的数据类型是构造数据类型
(3)由若干构造数据类型组合而成的数据类型是构造数据类型。 

‼️注:语言本身的这种能力使我们​​​​​​​能够定义非常复杂的数据结构,例如树 (tree)、链表 (list)和映射(map)等。 

🌸关键字struct 与 class 的困惑

C++语言对C 语言的 struct 进行了改造,使其也可以像class 那样支持成员函数的声明和定义,从而使struct 变成真正的抽象数据类型 (Abstract Data Type. ADT),这使得许多人对 struct 和 class 倍感困惑。
当语言支持某种特征时,是否使用这种特征则完全取决于程序员。因此,并不是说class 支持成员函数的定义,我们就一定要在每一个class 中都定义成员函数;也并不是说 struct 过去不支持成员函数定义,我们就非得用 class 完全取代struct。实际上就 C++语言本身来讲,struct 和 class 除了“默认的成员访问杖限”这一点不同外没有任何区别。
‼️注:在C++语言中,如果不特别指明,struct 成员的默认访问限定符为 public,而class 成员的默认访问限定符为 private.因此,在C++程序中,只要你明确地声明每一个成员的访问权限,那么完全可以用 struct 取代class,也完全可以用 class 取代struct。
‼️注:为了不让程序产生混乱和妨碍理解,建议还是使用struct定义简单数据集合,而定义一些具有行为的ADT时最好采用class

🌸使用struct

在C++环境中,我们把C 风格的struct 叫做 POD (Plain Old Data)对象,从字面上你也可以知道它仅包含一些数据成员,这些数据成员可以是基本数据类型变量任何类型的指针或引用、任何类型的数组及其他构造类型的对象等
‼️注:虽然把数组当做参数传递给函数的时候,数组将自动转换为指针,但是包装在struct/class 中的数组其内存空间则完全属于该struct/class 的对象所有。如果把struct/class 当做参数传递给函数时,默认为值传递,其中的数组将全部拷贝到函数堆栈中因此,当你的 UDT/ADT 中包含数组成员的时候,最好使用指针或引用传递该类型的对象,并且一定要防止数组元素越界,否则它会覆盖后面的结构成员。
‼️注:构造类型虽然可以嵌套定义,但是嵌套定义的类型对象不一定存在包含关系,存在包含关系的对象类型也不一定是嵌套定义的。

🐰位域

🏡前言

以字节为单位的存储模式会浪费大量的内存空间,位域以单个的位(bit)为单位来设计struct所需要的存储空间,因此你可以根据数据成员的有效取值范围来仔细规划他们各自所需要的位数
Struct Datatime
{
    Int year;
    Int month    :4;
    Int day         :5;
    Int hour       :5;
    Int minute    :6;
    Int second   :6;
}

C语言位域各成员的类型必须是    int,unsigned int, signed int等类型,c++还允许使用char,long

不允许使用指针类型和浮点类型作为位域的成员类型 。signed int等类型数据的正负符号要占用一位,因此该类型的位域成员的长度至少为2.

🤔提示

不要定义超越类型的最大位数的位域成员,例如
Struct DatdTime
{
    Unsigned int year   :33;//位越界
};

不仅会导致结果上溢,还会导致一个成员跨越两字节的边界却又不能占满字节,导致浪费

可以定义长度为0的位域成员,其作用是迫使下一个成员从下一个完整的机器字(word)开始分配空间

🤔提示

(1)位域成员的访问方法和结构成员的访问没有区别。
(2) 不能取一个位域对象的数据成员地址 ,即使该成员完全与字节边界对齐,因为字节是编址最小单位而不是位; 但是可以取位域对象的地址(跟取结构体对象地址一样) ,即使位域所有成员的总位数达不到整字节的倍数,位域对象也会对齐。
(3)不要把位域成员当作位的数组,因此不能使用访问数组的方法来访问位域成员的单个位。
注:使用位域节省存储空间会导致程序运行速度的下降,因为计算机无法直接寻址到单个字节中的某些位,必须通过额外的代码来实现。这种矛盾是由计算机的基本原理决定的,在“内存空间”和“运行速度”无法同时优化的情况下,由应用需求来决定优化哪一个

🌸成员对齐

​​​​​​​

你能说出 Sedan 的对象在内存中实际占据的字节数是多少吗?或者更直接地说,这个结构的大小是多少呢?这个结构定义中的数据成员的声明顺序或者说这个结构的成员布局是不是合理的呢?为什么?如果不合理,怎样调整才能既不损失数据成员的访问效率,又能使对象的内存占用量最少呢?许多人会说:把所有成员的大小加在一起不就行了(即15字节)吗?果真如此简单吗?更多的人会立刻说:用sizeof运算符让编译器帮我们计算不就行了吗?的确,任何时候都应该用 sizeof运算符来计算​​​​​​​一个类或者对象的大小,而不要自己猜测。但是如果仅仅想到这一步还不够!

✈️CPU 对对象的访问效率与什么有关系呢?是它们的地址的特点。
对于复合类型 (一般指结构和类)的对象,如果它的起始地址能够满足其中要求最严格 (或最高)的那个数据成员的自然对齐要求,那么它就是自然对齐的;如果那个数据成员又是一个复合类型的对象,则依次类推,直到最后都是基本类型的数据成员。
什么是“自然对齐要求最严格” 呢?举例来说吧:double 变量的地址要能够被 8整除,而int 变量的地址只需能被4 整除即可,一个bool 变量的地址则只需能被1整除。所以double 类型的自然对齐要求就要比 int 类型严格,int 类型的对齐要求又比bool 类型严格,因为能够被8 整除的地址肯定能被 4 整除,但是反过来就不一定了。在C++/C 的基本数据类型中,如果不考虑enum 可能的最大值所需的内存字节数,double 就是对齐要求最严格的类型了,其次是int 和float, 然后是short、bool
和 char。
例如,考虑 Sedan 的自然对齐要求。由于Sedan 的所有成员都是基本类型,显然double 成员m price 的对齐要求最严格,因此 Sedan 的对象的地址应该能被8整除。此外,如果编译器按照自然对齐的要求布局 Sedan 的内存映像的话,mprice 的偏移量还必须是8的倍数才能确保也总是自然对齐的,在这里应该是16字节;其他成员的末始地址也需要满足各自的自然对齐要求。
在复合类型的对象中,各个数据成员在内存中是如何排列的呢?一般说来,没有编译器会故意自找麻烦而把用户定义的数据成员声明顺序打乱来构造对象,都会直接依照声明顺序来存放,即使复合类型中存在多个访问段(即 C++类中的每个public、private 和protected 访问限定符),至少也会保证每个段内的所有数据成员是按照声明顺序来存放的。至于先声明的成员会被放在高地址还是低地址处,完全是由编译器实现来决定的,而且一般都会采用“按照声明的先后顺序从低地址到高地
址依次布放各个成员” 的方案。同时,为了满足各个成员的对齐要求,各个成员之间甚至对象的末尾可能会插入一定量的填充字节,因此对象的实际大小往往比把各个成员的大小简单加在一起要大。为什么有的对象会在末尾插入一定量的填充字节呢?因为编译器在考虑一个类型的大小的时候,不仅要考虑一个对象的对齐要求还要考虑该类型对象数组的对齐要求,这样才能保证用户在使用对象数组时也具有和单个对象一样的访问效率。注意:绝对不会在对象开头插入填充字节.
基于上述认识,假设 Sedan 的对象s的起始地址为 0x00031D10,则s 的内存布局将可能如图 8-1 所示(假设按照声明的先后顺序从低地址到高地址依次布放各个成员)

🌸结构内存大小的计算

结构体内存对齐的规则
1.第一个成员与结构体变量偏移量为0的地址处
2.其他成员变量要对齐到某个数(对齐数)的整数倍的地址处
3.对齐数=编译器默认的一个对齐数(8)与该成员大小的较小值
4.结构体的总大小是结构体的所有成员的对齐数中最大的那个对齐数的整数倍
5.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

🐰联合(Union)

🏡前沿

联合也是一种构造数据类型,它提供了一种不同类型数据成员间共享储存空间的方法,同时可以实现不同数据类型成员之间的自动类型转换。但是与结构不同的是,联合对象在同一时间只能存储一个成员的值(即只有一个数据库是活跃的)。因此,如果你同时访问一个联合对象的多个成员,那么其中最多只有一个值是正确的

🌸联合内存大小的计算

这些类型定义的变量也包含一系列的成员,特征是这些成员共用一块空间(也叫共用体)
联合体的大小:
1.至少是最大成员的大小
2.当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

🐰枚举(Enum)​​​​​​​

🏡前言

C++/C 枚举类型允许我们定义特定用途的一组符号常量,它表明这种类型的变量可以取值的范围。当你定义一个枚举类型的时候,如果不特别指定其中标识符的值,则第一个标识符的值将为 0,后面的标识符将比前面的标识符依次大1;如果你指定了其中某一个标识符的值,那么它后面的标识符自动在前面的标识符值的基础上依次加1,除非你也同时指定了它们的值。
​​​​​​​

🌸枚举类型的大小

在标准C中,枚举类型的内存大小等于 sizeof (int)。但是在标准C++中,枚举类型的底层表示并非必须是一个int————它可以更小或更大。换句话说,如果一个枚举变量的取值范围小到足以用一个short 或 byte 来表示,那么这个枚举变量的底层表示就可能采用 short 或 byte:相反如果一个枚举变量的取值范围大到必须用一个比 int更大的类型来表示的话,那编译器允许使用更大的类型来表示枚举变量

🤔‼️提示

枚举变量和常量都可以参与整型变量能够参与的某些运算,但注意不要给枚举变量赋予一个不在枚举常量列表中的值,例如,不要对枚举变量使用++,--,+=,-=等操作,除非你为它特别重载了这些运算符
虽然枚举是和整数类型兼容的数据类型,但是它们之间的转换还是有一些需要注意的地方。枚举类型变量一般可以直接转换成某种整数类型,除非其值超出了这种整数类型可以表示的范围。但是一个整型变量在强制转换成枚举类型后就不一定具有一个有效的值了,这是因为:整型数是连续的,而枚举类型变量的取值很可能是不连续的,因此当你把一个值不等于任何一个枚举常量的整型变量强制转换成这种枚举类型时,其结果就不得而知了。

🔥🔥🔥希望看完本篇后,加深大家对自定义类型的印象,如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🔥🔥🔥

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/195956.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot动态导出word文档(完美实整教程 复制即可使用,不能实现你找我)

背景 最近有一个需求是需要动态导出合同、订单等信息,导出一个word文档供客户进行下载查看。 需要导出的word文件,主要可以分为两种类型。 导出固定内容和图片的word文档导出表格内容不固定的word文档 经过对比工具,我实践过两种实现方式…

一文细说引导内存分配器

一、引导内存分配器 1.引导内存分配器的作用 因为内核里面有很多内存结构体,不可能在静态编译阶段就静态初始化所有的这些内存结构体。另外,在系统启动过程中,系统启动后的物理内存分配器本身也需要初始化,如伙伴分配器&#xff…

OD笔试题-空汽水瓶可以换汽水

/*** 某商店规定&#xff1a;三个空汽水瓶可以换一瓶汽水&#xff0c;允许向老板借空汽水瓶&#xff08;但是必须要归还&#xff09;。* 小张手上有n个空汽水瓶&#xff0c;她想知道自己最多可以喝到多少瓶汽水。* 数据范围&#xff1a;输入的正整数满足 1≤n≤100* <p>*…

springboot 多环境配置yml

创建多个配置文件 创建文件时注意&#xff0c;一定是 application-文件名称.yml 这种格式 application.yml #主配置文件 application-dev.yml #开发环境的配置 application-prod.yml #生产环境的配置application-prod.yml # 生产环境端口为90 server:port: 90applica…

Python实现将一段话txt生成字幕srt文件

Python实现将一段话txt生成字幕srt文件 作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; 一、为什么要将txt转换成字幕 1.1方便到剪辑软件剪辑 有时获取到一段文本&#xff0c;想要直…

点分治学习笔记

有时候我们会碰到一些树上的路径问题&#xff0c;如果需要处理的规模很大的话&#xff0c;这时候点分治是一个很好的工具&#xff0c;往往可以在O(nlogn)的复杂度内完成操作&#xff0c;一般用于离线处理问题 前置芝士 树的重心&#xff1a;最大子树的值最小的点叫做重心。 …

【手撕面试题】HTML+CSS(高频知识点二)

目录 面试官&#xff1a;页面导入样式时&#xff0c;使用 link 和 import 有什么区别&#xff1f; 面试官&#xff1a;简要说说 title与h1的区别、b与strong的区别、i与em的区别&#xff1f; 面试官&#xff1a;img标签的title和alt有什么区别&#xff1f; 面试官&#xff…

给特别规则FeignClient增加统一的RequestInterceptor

需求背景&#xff1a; 在微服务横行天下的今天&#xff0c;Spring Cloud OpenFeign 早就成了我们服务间调度的主要远程调用手段。 在Feign进行远程调度的时候&#xff0c;难免会做一些心跳&#xff0c;权限的校验&#xff0c;来保证调度过程的相对安全。 但对于外部系统来说…

Unity 之 Addressable可寻址系统 -- 资源远程加载 | 资源预下载 -- 进阶(三)

可寻址系统远程加载 -- 资源预下载 -- 进阶&#xff08;三&#xff09;一&#xff0c;Unity 云资源分发 -- 使用介绍1.1 CCD 的介绍1.2 后台准备工作二&#xff0c;CDD的使用2.1 CCD可视化界面的使用2.2 CDD命令行界面使用2.2.1 准备工作2.2.2 CLI 用法三&#xff0c;AA CCD资…

Java中的快速排序

快速排序递归版本挖坑法Hoare法优化非递归相信即使大家并不知道快速排序究竟是个啥,但也一定听说过快排,今天我来给兄弟们讲讲快速排序!递归版本 快速排序的思想就是找基准,就比如我们以数组中的第一个数字12为基准,我们从最后往前面找,如果找到一个比12小的数字就用它覆盖12,但…

Linux—InstallOS-RedHat9.1

下载https://developers.redhat.com/products/rhel/download 需注册账号。安装正常安装就行。安装注意事项&#xff1a;(1)Software SelectionCentOS的摘录过来&#xff0c;通用。最小安装&#xff08;Minimal Install&#xff09;这个选项只提供运行CentOS 的基本软件包。最小…

Python学习-----起步4(列表元素的添加,删除,修改,查询,获取长度)

目录 前言&#xff1a; 列表元素的添加&#xff08;或者叫写入&#xff09; 1.append&#xff08;&#xff09;函数 2.extend&#xff08;&#xff09;函数 3.insert()函数 列表元素的删除 1.remove() 函数 2. pop() 函数 3.clear&#xff08;&#xff09;函数 4.del …

公司40岁的程序員到底在写什么代码

去年在前公司玩了一年&#xff08;基本兩三個月一個需求&#xff09;&#xff0c;除了日常維護就一些特別簡單的功能開發&#xff0c;到年底也沒見到公司黃&#xff08;國企背景&#xff09;&#xff0c;沒辦法只好裸辭&#xff0c;現在這個公司各方面还不错&#xff0c;但是令…

Cookie、Session、Token、JWT只看这一篇文章就够了

什么是认证&#xff08;Authentication&#xff09; 通俗地讲就是验证当前用户的身份&#xff0c;证明“你是你自己”&#xff08;比如&#xff1a;你每天上下班打卡&#xff0c;都需要通过指纹打卡&#xff0c;当你的指纹和系统里录入的指纹相匹配时&#xff0c;就打卡成功&a…

MongoDB Map Reduce

在用 MongoDB 查询时&#xff0c;若返回的数据量很大&#xff0c;或者做一些比较复杂的统计和聚合操作做花费的时间很长时&#xff0c;可以使用 MongoDB 中的 mapReduce 进行实现。mapReduce 是个灵活且强大的数据聚合工具&#xff0c;它的好处是可以把一个聚合任务分解为多个小…

设计模式(三)----创建型模式之单例模式(一)

一、创建型模式 创建型模式的主要关注点是“怎样创建对象&#xff1f;”&#xff0c;它的主要特点是“将对象的创建与使用分离”。 这样可以降低系统的耦合度&#xff0c;使用者不需要关注对象的创建细节。 创建型模式分为&#xff1a; 单例模式 工厂方法模式 抽象工厂模式…

英语学习 3

1 词汇积累 1、ships 船 2、class 级 3、marvels 奇迹 4、marvelous 非凡的、了不起的、极好的 5、cursed 诅咒、被诅咒的 6、the most luxurious ships 最豪华的船 7、luxury 奢侈、奢华的 8、luxurious 心满意足的、舒适的 9、utmost 极度的、最大的 10、kind 种类 11、voya…

Kali Linux神秘工具教程(详细版)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、Kali Linux - 安装和配置信息收集工具二、NMAP隐形扫描搜索Searchsploit域名系统工具dnsenum.plDNSMAPdnstracerLBDHping3漏洞分析工具Cisco-torch工具Cisco…

回溯算法(基础)

目录 一、基本概念 二、以简单全排列认识回溯 &#xff08;一&#xff09;决策树 &#xff08;二&#xff09;回溯示意图 &#xff08;三&#xff09;核心代码 &#xff08;四&#xff09;完整代码 三、组合问题 &#xff08;一&#xff09;问题 &#xff08…

如何通过groovy扩展方法

最近一直使用jmeter做接口测试&#xff0c;虽然好用&#xff0c;但是每次解析结果都要写大量重复代码。然后想到groovy是可以在运行时动态增强jvm字节码的&#xff0c;比如Date中就有大量增强的方法&#xff0c;比如format,upto,downto......&#xff0c;既然groovy可以&#x…