自定义类型讲解

news2024/11/18 16:53:58

 

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

作者: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* p
	printf("%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,//0
	GREEN,//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));//4
	return 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("小端");
	else
		printf("大端");
	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("小端");
	else
		printf("大端");
	return 0;
}

 

 五.总结

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

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

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

相关文章

大家做性能测试都用什么工具

在进行测试时,选择适合的测试工具至关重要,因为优秀的测试工具能够显著提高工作效率。对于性能测试和自动化测试而言,大多数人会选择传统的JMeter等工具,然而这些工具存在学习成本高、使用门槛高的问题。 因此,我在这…

微信小程序开发学习之--地图绘制行政区域图

不知道大家有没有感觉就是在做微信小程序地图功能时刚刚接触时候真的感觉好迷茫呀,文档看不懂,资料找不到,就很难受呀,比如我现在的功能就想想绘制出一个区域的轮廓图,主要是为了显眼,效果图如下&#xff1…

官方Office 技巧免费学习平台-WPS学堂

WPS学堂是WPS官方Office 技巧免费学习平台,目前网站累计上线 3000个免费教学视频图文,包含WPS表格(Excel)、WPS文字(Word)、WPS演示(PPT)的操作技巧及新手入门系列课视频,而且教学视频都可以直接在线学习,不…

14.2 【Linux】软件磁盘阵列(Software RAID)

14.2.1 什么是 RAID 磁盘阵列全名是“ Redundant Arrays of Inexpensive Disks, RAID ”,英翻中的意思是:容错式廉价磁盘阵列。 RAID 可以通过一个技术(软件或硬件),将多个较小的磁盘整合成为一个较大的磁盘设备&…

图数据库Neo4j学习四——Spring Data NEO

1配置 1.1Maven依赖 <!--neo4j --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-neo4j</artifactId> </dependency>1.2yml配置 spring:data:neo4j:uri: bolt://localhost:76…

【机器学习】Cost Function for Logistic Regression

Cost Function for Logistic Regression 1. 平方差能否用于逻辑回归&#xff1f;2. 逻辑损失函数loss3. 损失函数cost附录 导入所需的库 import numpy as np %matplotlib widget import matplotlib.pyplot as plt from plt_logistic_loss import plt_logistic_cost, plt_two_…

利用易查分制作分班查询系统,怎么导入数据?

暑假过半&#xff0c;新学期即将到来&#xff0c;这对学校来说是一个重要的时刻。新学期的开始意味着学校将面临新生入学和老生升入高年级的情况&#xff0c;这就需要进行分班工作的安排。分班工作是一项繁琐而关键的任务&#xff0c;它直接关系到学生们在新学期中的班级和同学…

【Linux进程篇】进程概念(1)

【Linux进程篇】进程概念&#xff08;1&#xff09; 目录 【Linux进程篇】进程概念&#xff08;1&#xff09;进程基本概念描述进程-PCBtask_struct-PCB的一种task_ struct内容分类 组织进程查看进程通过系统调用获取进程标示符通过系统调用创建进程——fork初识 作者&#xff…

SpringMVC源码分析 —— 拦截器是何时调用的

SpringMVC源码分析&#xff0c;拦截器是何时、以什么方式调用的&#xff1f;本文将进行详细说明 环境准备 springboot 2.3.7.RELEASE 笔者创建一个springboot的web项目&#xff0c;使用的springboot的版本是2.3.7.RELEASE 对应的spring-web版本是5.2.12.RELEASE 下面将对上面…

【NLP-新工具】语音转文本与OpenAI的用途

一、说明 OpenAI最近2022发布了一个名为Whisper的新语音识别模型。与DALLE-2和GPT-3不同&#xff0c;Whisper是一个免费的开源模型。它的主要功能就是将语音翻译成文本。本文将介绍如何使用这个重要应用库。 二、 Whisper概念 2.1 Whisper是啥&#xff1f; Whisper 是一种自动…

考完软考,有什么备考心得和学习经验可以分享吗?

恭&#xfffd;&#xfffd;您完成软考考试&#xff01;备考软考确实是一项艰苦的任务&#xff0c;但也是一次很有收获的学习和成长过程。下面分享一些备考心得和学习经验&#xff0c;以及针对系统集成项目管理工程师和信息系统项目管理师考试的备考建议&#xff1a; 备考心得…

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

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

【Android】APP网络优化学习笔记

网络优化原因 进行网络优化对于移动应用程序而言非常重要&#xff0c;原因如下&#xff1a; 用户体验&#xff1a; 网络连接是移动应用程序的核心功能之一。通过进行网络优化&#xff0c;可以提高应用的加载速度和响应速度&#xff0c;减少用户等待时间&#xff0c;提供更流…

跨境电商还有人在做吗,这十大选品技巧建议收藏!

随着电商的快速发展&#xff0c;无论国内或者国外电商&#xff0c;竞争都比较激烈&#xff0c;很多人觉得现在入行太晚了&#xff0c;玩不过那些老卖家。 不过我想说的是&#xff1a;做电商很重要的一点就是选品&#xff0c;那些很早一批老卖家可能也是借着红利期走过来的&…

ATFX汇市:美联储加息25基点,虽提振美指,或招致衰退

环球汇市行情摘要—— 上周&#xff0c;美元指数上涨0.6%&#xff0c;收盘在101.7点&#xff0c; 欧元贬值0.96%&#xff0c;收盘价1.1017点&#xff1b; 日元升值0.47%&#xff0c;收盘价141.16点&#xff1b; 英镑贬值0.01%&#xff0c;收盘价1.2852点&#xff1b; 瑞郎…

微信小程序配置上传多个u-upload上传

微信小程序配置上传多个u-upload上传 使用的是uView框架 微信小程序配置上传多个u-upload上传图片 场景需求&#xff1a;根据PC端配置项追加图片配置 小程序根据配置的图片数量&#xff0c;图片名称&#xff0c;进行上传图片 难度在于 我们不知道用户会追加多少个图片配置字段 …

预测性维护和预防性维护的区别

预测性维护和预防性维护是两种不同的设备维护策略&#xff0c;它们在维护时机、方法和效果上存在明显的区别。在工业生产和设备管理中&#xff0c;选择适合的维护方式对于提高设备的可靠性、延长寿命以及降低维护成本至关重要。本文将深入探讨预测性维护和预防性维护的区别及其…

【LeetCode】剑指offer礼物的最大价值

礼物的最大价值 题目描述算法分析编程代码 链接: 礼物的最大价值 题目描述 算法分析 编程代码 class Solution { public:int maxValue(vector<vector<int>>& grid) {int m grid.size();int n grid[0].size();vector<vector<int>> dp(m1,vector…

开利网络受邀参与战略合作伙伴和合控股 聚焦数据价值

近日&#xff0c;开利网络战略合作伙伴和合控股组织的“数据聚焦价值&#xff0c;引导数据化转型”主体研讨会&#xff0c;开利网络作为和合控股的战略合作伙伴和“数利丰”应用技术合作方&#xff0c;受邀进行落地应用分享。 随着“数字中国”战略的提出&#xff0c;数据已然成…

【爬虫逆向案例】某易云音乐(评论)js逆向—— params、encSecKey解密

声明&#xff1a;本文只作学习研究&#xff0c;禁止用于非法用途&#xff0c;否则后果自负&#xff0c;如有侵权&#xff0c;请告知删除&#xff0c;谢谢&#xff01; 【爬虫逆向案例】某易云音乐&#xff08;评论&#xff09;js逆向—— params、encSecKey解密 1、前言2、行动…