自定义类型讲解

 

💕痛苦难道是白忍受的吗?💕

作者:Mylvzi 

 文章主要内容:自定义类型讲解 

一.结构体

定义:

数组:多组相同类型元素的集合

结构体:多组不同类型元素的集合-->管理多组不同类型数据的集合体,结构体中的数据也叫做结构体成员。

例如:管理学生的基本信息,需要的数据有学生的年龄,性别,身高等等

结构体关键字:struct

结构体的声明:

struct Stu//创建了一个结构体类型-->struct Stu-->整体是一种数据类型
{int age;float height;char name[20];
}s1,s2;//可直接在末尾添加你所需要的变量名struct Stu s1, s2;//使用类型创建变量  类型+变量名  int a;

一种特殊的声明:匿名声明(忽略掉tag标签)

//特殊的声明-->匿名声明-->不告诉你具体名字
struct {int a;float b;
}x;//匿名定义了一个结构体变量x
//缺点:只能使用一次,无法对其修改
//优点:安全性高//注意:未知tag,保证了其使用的唯一性
struct
{int a;char c;float f;
}x;struct 
{int a;char c;float f;
}* p;int main()
{p = &x;//err//尽管成员列表相同,但都是匿名结构体变量,未知类型,会发生类型转换报错return 0;
}

结构体的自引用:

//结构体的成员列表不能存在一个类型和该结构体一样的结构体
//套娃是非法的;无法计算具体的大小

但可以有和原结构体类型相同的结构体指针变量,指向下一个结构体;(链表中常使用)


//通过结构体访问下一个结构体
struct Node
{int data;struct Node next;//err//sizeof(struct Node)是多少?无法计算
};//改进
struct Node
{int data;struct Node* next;//存放下一个结构体的地址
};int main()
{printf("%zd\n", sizeof(struct Node));return 0;
}//错误的命名方式
typedef struct
{int data;Node* next;
}Node;
//先typedef为Node后才能使用Node,不能直接在成员列表内使用typedef struct Node
{int data;struct Node* next;
}Node;

尽量不要使用匿名的方式声明结构体,可能声明错误; 

结构体定义和初始化:

注意:使用.操作符初始化结构体时,可以不按照顺序初始化;否则,一定要严格按照结构体成员顺序进行初始化 

struct SN
{char c;int i;
}sn1 = { 'q', 100 }, sn2 = {.i=200, .c='w'};//全局变量

结构体的内存对齐(重要): 

先来计算两个结构体的大小:

再来看成员相较于结构体初始地址的偏移量(利用到offsetof宏) 

  

通过以上两个现象,我们知道,结构体成员在内存中存储时并不是连续存储的,且其大小也不能简单的通过成员大小加和的方式得到;实际上,结构体在内存中的存储以及其大小是有一定的规则,这个规则叫做结构体内存对齐 

结构体内存对齐规则:

1.结构体第一个成员的起始地址总是位于结构体偏移量为0的地址处;

2.从第二个成员开始,剩下的成员在内存中存放时要对齐到其对齐数的整数倍处;

对齐数:默认对齐数和成员自身大小的较小值,vs的默认对齐数8,Linux中无默认对齐数,对齐数是成员大小本身

3.结构体内存大小:必须是最大对齐数的整数倍

4.嵌套结构体:如果一个结构体嵌套了一个结构体,嵌套的结构体在内存对齐时对齐到其最大对齐数的整数倍处,整个结构体的内存大小是最大对齐数的整数倍(含嵌套的结构体的对齐数) 

 利用内存对齐规则分析上述两个结构体的内存分布及内存大小:

为什么要进行内存对齐呢?

有两个原因:
1.平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。硬件不同,读取数据的方式不同,读取到的内容也就不同,通过内存对齐可以实现跨硬件读取数据;

2.性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总而言之,结构体内存对齐是一种拿空间换时间的做法,尽管浪费掉了一些内存空间,但我们访问数据的速度大大提升;

 但是,我们也可以做到结构体空间的最优化-->将内存空间小的数据集中在一起,比如s1,s2

修改默认对齐数:

#pragma预处理指令

#pragma pack(16)//修改默认对齐数为16
struct Stu
{int i;char c1;char c2;
};
#pragma pack()//恢复默认对齐数

结构体传参: 

传递结构体时,尽量传递结构体地址(使用结构体指针接收)

struct S
{int data[1000];int num;
};void print1(struct S p)//传递结构体本身
{printf("%d\n", p.num);//形参是实参的临时拷贝,传递结构体本身会重新开辟一块儿内存空间
}void print2(struct S* p)//传递结构体地址  //如果不希望p所指向的内容被改变,添加const修饰
{                                       //const struct S* pprintf("%d\n", p->num);
}
//print2效率更高,减少了空间的开辟。提高效率;
int main()
{struct S s1;print1(s1);print2(&s1);return 0;
}

二.位段 

位段的定义及内存分配

讲完结构体就需要讲一下结构体实现位段的能力

位段-->给成员分配具体大小内存空间的结构体

注意:

1.声明和结构体相同

2.成员必须是int,unsigned int,char类型

3.设计格式:成员类型 成员名:具体大小

4.位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。

 

 观察下列位段在内存中的分配:

位段的跨平台问题:

1. int 位段被当成有符号数还是无符号数是不确定的。

2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。

3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。

 位段的应用: 

不难发现,位段是一种对空间极度优化的结构体,往往应用到空间利用率高的数据存储,或者大量开关信息的存储;

举例:信息的传递(ip数据包的传递)

​​​​​​​

三.枚举

定义:

枚举也是一种存储数据的自定义类型,顾名思义,如果取值能够被一一枚举,那么我们就可以使用枚举来存储相应的数据

枚举关键字:enum

enum Sex//性别
{MALE,FEMALE,SECRET
};
enum Color//颜色
{//都有默认取值RED,//0GREEN,//1 GREEN = 5;也可以人为赋值BLUE//2
};
//enum Color,enum Sex都是枚举类型

优点: 

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

        更加规范,代码量少;便于维护

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

        只能使用枚举类型的数据进行赋值,否则会报错

3. 便于调试

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

enum Color//颜色
{RED = 1,GREEN = 2,BLUE = 4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5;//ERR

枚举的应用(来源于chatgpt)

四.联合体(共用体)

定义:

联合体也是一种存储多种数据的自定义类型,其特点是所有的成员共用同一块内存(所以也叫共用体)

联合体关键字:union

//联合体
union Un
{int a;char b;
};
int main()
{printf("%d\n", sizeof(Un));//4return 0;
}

特点:

所有成员共用同一块儿空间

 

联合体大小计算:

1.内存大小至少是最大成员的内存大小(必须能够保存该数据)

2.且最终大小要是最大对齐数的整数倍

 利用联合体检验当前计算机存储方式(大小端的检验)

//大小端的检验
//之前写法
//检查首地址元素的值
int check_sys(int* p)
{int b = *(char*)p;//得到首地址return b;
}
int main()
{int a = 1;int ret = check_sys(&a);if (ret == 1)printf("小端");elseprintf("大端");return 0;
}int check_sys()
{union{int i;char c;}un = {un.i=1};return un.c;
}
int main()
{int ret = check_sys();if (ret == 1)printf("小端");elseprintf("大端");return 0;
}

 

 五.总结

  这篇文章详细介绍了四种自定义类型,结构体(struct),位段(指定成员大小的结构体),枚举类型(enum),联合体(union);要掌握他们的声明,定义方式,在内存中的存储方式,以及内存大小的计算;尽管在目前学习中,自定义类型的应用场景较少,但在之后的学习中会大量使用

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

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

相关文章

计算机视觉实验:人脸识别系统设计

实验内容 设计计算机视觉目标识别系统,与实际应用有关(建议:最终展示形式为带界面可运行的系统),以下内容选择其中一个做。 1. 人脸识别系统设计 (1) 人脸识别系统设计(必做):根据…

tinkerCAD案例:24. Ruler - Measuring Lengths 标尺 -量勺

tinkerCAD案例:24. Ruler - Measuring Lengths 标尺 - 测量长度 Project Overview: 项目概况: A machine shop, where any idea can become a reality, can cost millions and million of dollars. Still, the most important tool in the shop is the…

vue-cli4升级到vue-cli5的踩坑记录

前言 最近对部分项目升级了vue-cli脚手架,记录一下 问题一: scss/less/css中无法引入public下的静态资源 问题描述 在样式文件中使用静态资源路径导致编译无法通过 错误信息如下: Module not found: Error: Cant resolve /img/login/lo…

小研究 - 主动式微服务细粒度弹性缩放算法研究(二)

微服务架构已成为云数据中心的基本服务架构。但目前关于微服务系统弹性缩放的研究大多是基于服务或实例级别的水平缩放,忽略了能够充分利用单台服务器资源的细粒度垂直缩放,从而导致资源浪费。为此,本文设计了主动式微服务细粒度弹性缩放算法…

Android 面试题 应用程序结构 十

🔥 Intent 传递数据 🔥 Activity、Service、BroadcastReceiver之间的通信载体 Intent 来传递数据。而ContentProvider则是共享文件。 Intent可传递的数据类型: a. 8种基本数据类型(boolean byte char short int long float double…

如何配置一个永久固定的公网TCP地址来SSH远程树莓派?

文章目录 如何配置一个永久固定的公网TCP地址来SSH远程树莓派?前置条件命令行使用举例:修改cpolar配置文件 1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 …

第1集丨Vue 江湖 —— Hello Vue

目录 一、简介1.1 参考网址1.2 下载 二、Hello Vue2.1 创建页面2.2 安装Live Server插件2.4 安装 vue-devtools2.5 预览效果 一、简介 Vue(读音 /vjuː/, 类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设…

app自动化测试之Appium问题分析及定位

使用 Appium 进行测试时,会产生大量日志,一旦运行过程中遇到报错,可以通过 Appium 服务端的日志以及客户端的日志分析排查问题。 Appium Server日志-开启服务 通过命令行的方式启动 Appium Server,下面来分析一下启动日志&#…

使用web-view实现网页端和uni-app端是数据传输

要实现这个功能 第一步&#xff1a;要在vue的public文件夹下面引入 <script type"text/javascript" src"https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script> 第二步&#xff1a;建立一个新的空的uni-app项目…

DHorse v1.3.0 发布,基于k8s的发布平台

综述 DHorse是一个简单易用、以应用为中心的云原生DevOps系统&#xff0c;具有持续集成、持续部署、微服务治理等功能&#xff0c;无需安装依赖Docker、Maven、Node等环境即可发布Java、Vue、React应用&#xff0c;主要特点&#xff1a;部署简单、操作简洁、功能快速。 新增特…

Apache Doris 巨大飞跃:存算分离新架构

作者&#xff1a;马如悦 Apache Doris 创始人 历史上&#xff0c;数据分析需求的不断提升&#xff08;更大的数据规模、更快的处理速度、更低的使用成本&#xff09;和计算基础设施的不断进化&#xff08;从专用的高端硬件、到低成本的商用硬件、到云计算服务&#xff09;&…

JPEG有损图像压缩编码器(附源码)

概述 一个基本由自己实现的JPEG有损图像压缩编码器&#xff0c;基于JFIF&#xff08;JPEG文件交换格式&#xff09;标准&#xff1a; 色彩空间转换&#xff08;RGB to YUV&#xff09;色度抽样&#xff08;采样因子4:2:0&#xff09;MCU分块&#xff08;16x16的最小编码单元&…

多模态第2篇:MMGCN代码配置

一、Windows环境 1.创建并激活虚拟环境 #创建虚拟环境命名为mmgcn&#xff0c;指定python版本为3.8 conda create -n mmgcn python3.8 #激活虚拟环境 conda activate mmgcn2.安装pytorch #torch2.0.0 cu118 pip install torch2.0.0cu118 torchvision0.15.1cu118 torchaudio…

PACS系统源码:支持三维重建功能、集成放射科管理RIS系统、图文报告编辑、打印、多级审核机制

PACS系统源码 PACS系统是以最新的IT技术为基础&#xff0c;遵循医疗卫生行业IHE/DICOM3.0和HL7标准&#xff0c;开发的多功能服务器和阅片系统。通过简单高性能的阅片功能&#xff0c;支持繁忙时的影像诊断业务&#xff0c;拥有保存影像的院内Web传输及离线影像等功能&#xf…

【雕爷学编程】MicroPython动手做(11)——搭建掌控板IDE开发环境四种

为了能够打好基础&#xff0c;系统学习MicroPython&#xff0c;特地入手了二块掌控板 知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通…

【Docker 学习笔记】Docker架构及三要素

文章目录 一、Docker 简介二、Docker 架构1. Docker 客户端和服务器2. Docker 架构图3. Docker 运行流程图 三、Docker 三要素1. 镜像&#xff08;Image&#xff09;2. 容器&#xff08;Container&#xff09;3. 仓库&#xff08;Repository&#xff09; 一、Docker 简介 Dock…

2.4 传统经验光照模型详解

一、光照模型 光照模型&#xff08;illumination model&#xff09;&#xff0c;也称为明暗模型&#xff0c;用于计算物体某点处的光强&#xff08;颜色值&#xff09;。从算法理论基础而言&#xff0c;光照模型分为两类&#xff1a;一种是基于物理理论的&#xff0c;另一种是…

【MATLAB第61期】基于MATLAB的GMM高斯混合模型回归数据预测

【MATLAB第61期】基于MATLAB的GMM高斯混合模型回归数据预测 高斯混合模型GMM广泛应用于数据挖掘、模式识别、机器学习和统计分析。其中&#xff0c;它们的参数通常由最大似然和EM算法确定。关键思想是使用高斯混合模型对数据&#xff08;包括输入和输出&#xff09;的联合概率…

<Doc>Windows常见的doc命令

一&#xff1a;管理员身份运行cmd命令&#xff1a; 方式一&#xff1a;搜索框输入cmd&#xff0c;回车&#xff0c;点击&#xff1a;以管理员身份运行 出现如图所示&#xff1a; 方式二&#xff1a;快捷键运行方式&#xff1a; 1.按winr&#xff0c;在运行窗口中输入cmd。 …

JavaSE类和对象(重点:this引用、构造方法)

目录 一、类的定义方式以及实例化 1.面向对象 Java是一门纯面向对象的语言(Object Oriented Program&#xff0c;简称OOP)&#xff0c;在Java的世界里一切皆为对象。 2.类的定义和使用 1.在java中定义类时需要用到class关键字 3.类的实例化 4.类实例化的使用 二、this引用 …