自定义类型

news2024/12/23 8:08:47

目录

什么是自定义类型

结构体

结构体的声明

常规结构体的声明形式

特殊的结构体声明形式 

匿名结构体:

匿名结构体的重命名:

注意事项:

结构体的自引用

什么是结构体的自引用

结构体变量的定义与初始化

方法一:

方法二:

方法三:

结构体内存对齐

结构体传参

位段

 枚举

枚举的声明

枚举的优点

枚举的使用

联合体

联合体的声明

大小的特点:

大小的计算:


什么是自定义类型

我们之前学过很多种类型,有int、char、float、double等等,这些都是属于既定类型,那么自定义类型顾名思义就是可以由你自己来定义的类型,比如我们接下来要讲的结构体、联合体、枚举都属于自定义类型。

结构体

首先我们来介绍结构体。

结构体的字面上的意思就是一个成结构的体系,因此它当然离不开组成它的部分了,而这些部分,就是结构体里边各种类型的成员。

结构体的声明

常规结构体的声明形式

我们这里创建了一个以学生信息为组成部分的结构体,我们可以看到一个简单的结构体是由哪些方面组成的。

①首先结构体的创建是由这样一个形式开始的,即struct+结构体类型名,而这里,结构体类型名就是stu这个名字是自定义的,可以叫a也可以叫b,由于我们要创建一个关于学生的结构体,所以我选择了stu,即student的缩写,作为类型名。而这个特点就是我们标题所说的自定义类型的体现

②接着我们还可以看到,在这个结构体的内部,包含了多个信息,即学生的姓名、年龄、班级、学号(如果需要的话,你可以再多加些信息,比如年级、性别等等)。这些信息的类型有char、int。当然,如果你愿意,你也可以在里边创建其他类型,比如double、float,甚至还可以再创建一个struct+类型名,形成结构体的嵌套,而这是后话了。

③细心的你可能还发现了,在这个结构体的最后,多出来个分号,而这个分号是不能缺少的,这似乎表明了在 };之间还可以有东西放进去,没错,在结构体的最后,还有结构体的变量,如图


初学易错点:
有人可能会觉得奇怪,为什么在创建结构体的开头已经有了stu,后面却又可以额外加s1,s2,s3。之所以会有这样的困惑,是因为他们习惯性地把我们之前创建变量的直观思维代入到了结构体上,简单来说就是,我们之前创建变量的形式是这样的:int a 即:类型+变量名,而我们又很容易把这样的形式套到结构体上,从而以为struct stu也是类型+变量名,但实际上这里的stu才是类型,也就是说这里stu的地位其实就相当于int,那结构体里边的变量在哪呢?那很显然就是那些加在后面的s1,s2,s3.

但是话说回来,像我们开始时如下图这样创建的时候:


这里并没有设定像s1,s2,s3这样的变量,而是什么都没有,};之间只是空着,那么这样是否违背了语法呢?其实并没有。 如图:


这里我们可以很直观明了地看到,像s1,s2,s3这样的变量可以之后创建。这说明在};之间先不创建变量,在后面需要时再另外创建变量是符合语法的。但这里有着全局变量与局部变量的区别,使用时要特别注意这个细节。

特殊的结构体声明形式 

匿名结构体:

struct
{
int a ;
char b ;
float c ;
} x ;
struct
{
int a ;
char b ;
float c ;
} a [ 20 ], * p ;

在这里,结构体的声明省略了类型名,并且这样做是符合语法的,这样的结构体叫做匿名结构体,由于没有类型名,它在之后不能再用来创建局部变量,所以这样的结构体一般只能作为一次性消耗品

匿名结构体的重命名:

如图,利用typedef可以将匿名结构体重新命名,使其可以利用新的类型名去创建变量,但是要注意,虽然同样是放在};之间,但上图中的stu是类型名! 

注意事项:

1)在创建匿名结构体的时候,不能再像创建常规结构体那样,把变量名省略,像这样:

虽然这样的创建方式在编译时不会报错(可能会警告),但是这样的结构体是没有用的!因为在创建时,它本身省略掉了类型名,而现在如果还要把变量名省略,那么它在之后也不可能再创建变量了,也就是不可能像下图这样: 

道理很简单,虽然这张图里在创建结构体时省去了变量名,但是仍然可以凭借stu这个类型名在之后创建局部变量s1,s2,s3。但这对于匿名结构体来说却是不可能的,因为它连类型名也没有。

关于一些错误用法

一般来说,不同类型的结构体如果设定了相同的变量名,那么就会出现重定义,从而导致编译器报错,就像这样:

但是匿名结构体并没有定义类型名,那是不是就可以看作同一类型的结构体,从而定义相同的变量了呢?

答案是不行!

结构体的自引用

什么是结构体的自引用

结构体的自引用,简单来说,就是在结构体的内部,包含一个和该结构体类型相同的成员。

在结构体内部再创建一个结构体,并且两者要类型一致

浅谈单链表
为什么要提到链表呢?因为最能直观体现出结构体自引用的就是单链表,我们不要被这个抽象的表达给唬住,单链表的原理十分简单。

我们在上文有说,结构体之内其实可以再创建结构体,利用这个原理,搭配指针,我们就可以实现链表的创建。

如图:

 

结构体变量的定义与初始化

方法一:

方法二:

方法三:

结构体内存对齐

一般情况:
本环节是理解结构体的储存原理的关键,掌握后你就会明白结构体大小计算的规律。在开始之前,请大家先思考一下,下图输出的结果会是什么?


我想很多人的回答通常都会是:6 6 13(char、int、double的字节大小分别为1 4 8)

但是我们运行一下,出来的结果却是这样:


很反直觉对吧?要搞清楚这个问题,我们就要了解结构体内存对齐这个重要性质。其实这个知识点并不复杂,针对上图的练习3,下面一张图带你掌握:

小技巧:让占用空间小的成员尽量集中在一起,这样做可以在节省时间的同时最大限度地减小空间的浪费

修改默认对齐数
#include <stdio.h>
#pragma pack(8) // 设置默认对齐数为 8
struct S1
{
char c1 ;
int i ;
char c2 ;
};
#pragma pack() // 取消设置的默认对齐数,还原为默认
#pragma pack(1) // 设置默认对齐数为 1
struct S2
{
char c1 ;
int i ;
char c2 ;
};
#pragma pack() // 取消设置的默认对齐数,还原为默认
int main ()
{
    // 输出的结果是什么?
printf ( "%d\n" , sizeof ( struct S1 ));
  printf ( "%d\n" , sizeof ( struct S2 ));
    return 0 ;
}

结论: 

结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。

结构体传参

struct S
{
int data [ 1000 ];
int num ;
};
struct S s = {{ 1 , 2 , 3 , 4 }, 1000 };
// 结构体传参
void print1 ( struct S s )
{
printf ( "%d\n" , s . num );
}
// 结构体地址传参
void print2 ( struct S * ps )
{
printf ( "%d\n" , ps -> num );
}
int main ()
{
print1 ( s );   // 传结构体
print2 ( & s ); // 传地址
return 0 ;
}
上面的 print1 print2 函数哪个好些?
答案是:首选 print2 函数。
原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

 结论:

结构体传参的时候,要传结构体的地址。

位段

位段的声明和结构体是类似的,有两个不同:

位段的成员必须是 int、unsigned int 或signed int 。

位段的成员名后边有一个冒号和一个数字。

如图:


A就是一个位段。 

计算:位段是按比特位的大小来进行计算的,比如一个字节有8个比特位,存入的时候根据数据类型的不同计算出相应的比特位依次往后存储,若存储不下则开辟下一个字节

举个例子:

 

那么8这个数字是怎么来的呢?是这样的:

int 型具有四个字节,共32个比特位,故存储时将前三个放入第一块空间中,由于第四个是30个比特位存储不下,因此开辟下一块空间存储,即开辟了两块空间,就是好8个字节。

注意:

和结构体相比,位段可以达到同样的效果,但是可以很好地节省空间,但是有跨平台的问题存在。

跨平台问题:

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

2)位段中最大的数目不能确定。(16位机器最大16,32位机器最大32)

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

 枚举

枚举的声明

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

比如我们现实生活中:

一周的星期一到星期日是有限的7天,可以一一列举

性别有:男、女、保密,也可以一一列举。

月份有12个月,也可以一一列举

这里就可以使用枚举了。先上代码:

enum Day // 星期
{
Mon ,
Tues ,
Wed ,
Thur ,
Fri ,
Sat ,
Sun
};
enum Sex // 性别
{
MALE ,
FEMALE ,
SECRET
}
enum Color // 颜色
{
RED ,
GREEN ,
BLUE
};

enum Day、enum Sex、enum Color都是枚举类型。

而{}里边的是枚举的可能取值,叫做枚举常量

这些可能取值都是有值的,默认从0开始,一次递增1。

当然,在定义的时候也可以赋初值。像这样:

enum Color // 颜色
{
RED = 1 ,
GREEN = 2 ,
BLUE = 4
};

枚举的优点

我们可以使用 define 定义常量,为什么非要使用枚举?

原因如下:

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

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

3. 防止了命名污染(封装)

4. 便于调试

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

枚举的使用

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

只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。

联合体

联合也是一种特殊的自定义类型

这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。

比如:

// 联合类型的声明
union Un
{
char c ;
int i ;
};
// 联合变量的定义
union Un un ;
// 计算连个变量的大小
printf ( "%d\n" , sizeof ( un ));

联合体的声明

大小的特点:

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联

合至少得有能力保存最大的那个成员。

union Un
{
int i ;
char c ;
};
union Un un ;
// 下面输出的结果是一样的吗?
printf ( "%d\n" , & ( un . i ));
printf ( "%d\n" , & ( un . c ));
// 下面输出的结果是什么?
un . i = 0x11223344 ;
un . c = 0x55 ;
printf ( "%x\n" , un . i );

大小的计算:

当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

union Un1
{
char c [ 5 ];
int i ;
};
union Un2
{
short c [ 7 ];
int i ;
};
// 下面输出的结果是什么?
printf ( "%d\n" , sizeof ( union Un1 ));
printf ( "%d\n" , sizeof ( union Un2 ));

以上就是我对自定义类型的相关讲解,讲解有不清楚或者讲错的地方,可以在评论区或者私信.看完记得点赞哦!!!  

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

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

相关文章

总结python安装包(库)过程中的采坑

绝大数的包比如numpy、pandas可以用pip install或者conda install解决&#xff0c;使用pip时可以用pip -V命令看一下自己的pip安装在了哪个虚拟环境&#xff0c;一般pip安装在哪默认就把python包安装在哪。 pip -VC:\Users\20478>pip -V pip 23.1.2 from D:\Python\lib\sit…

Android Java代码与JNI交互 JNI子线程访问Java方法 (八)

🔥 Android Studio 版本 🔥 🔥 创建包含JNI的类 JNIInvokeMethod.java 🔥 package com.cmake.ndk1.jni;import com.cmake.ndk1.base.ICallbackMethod; import com.cmake.ndk1.base.IThreadCallback;public class JNIInvokeMethod {static {System.loadLibrary("…

VBA代码如何切换word和excel(3)

【分享成果&#xff0c;随喜正能量】人不能因为一件好事&#xff0c;高兴一整年&#xff0c;却能因为一个创伤&#xff0c;郁郁终生。痛苦给人的刺激&#xff0c;总是远远大于快乐。成年人的烦恼&#xff0c;和谁说都不合适&#xff0c;悲喜自渡&#xff0c;他人难悟。人最强大…

DDOS防御,阻止DDoS攻击的15个独家技巧

DDoS攻击可以使企业完全宕机数小时以上&#xff0c;而宕机的后果可能很严重&#xff0c;各种规模的企业和政府都可能受到影响。2021年&#xff0c;由于系统中断一小时导致销售额大幅下降&#xff0c;亚马逊为此遭受了约3400万美元的直接财务损失。而随后由于Fakebook的服务中断…

Spring源码系列-第2章-后置工厂处理器和Bean生命周期

第2章-后置工厂处理器和Bean生命周期 后置工厂处理器属于后置处理器&#xff0c;后置处理器是Spring最核心的部分&#xff0c;Spring几乎所有的附加功能全由它完成。 什么是BeanPostProcessor&#xff1f; public interface BeanPostProcessor {/*** Apply this {code BeanPos…

桥接(Bridge)模式

目录 动机使用场景参与者优劣协作实现相关模式应用和思考 桥接模式是将抽象部分和它的实现部分分离&#xff0c;使他们都可以独立的变化的对象结构型模式。桥接模式通过将继承改为组合的方式来解决问题&#xff1b;具体来说就是抽取其中一个维度并使之成为独立的类层次。 动机…

gma 2 教程(二)数据操作:2. 功能逻辑架构和栅格数据类型简介

功能逻辑架构 gma栅格数据操作所含功能/属性的关系结构如下图所示&#xff1a; 栅格数据类型 gma栅格数据类型继承自GDAL&#xff0c;与NumPy数据关联&#xff0c;但又有所不同&#xff0c;详细关系见下表&#xff1a; 栅格格式支持 栅格格式信息统计 gma继承了GDAL全部的栅格…

详谈三次握手

作者&#xff1a;爱塔居 专栏&#xff1a;计算机网络 作者简介&#xff1a;大三学生&#xff0c;希望和大家一起进步 经过三次的对话&#xff0c;这两个火柴人才确认了双方都能够说话&#xff0c;都能听见。三次握手也是一样的&#xff0c;只要这样才能确认双方的接受与发送能力…

文件操作--按数据块读写文件

函数fread&#xff08;&#xff09;和函数fwrite&#xff08;&#xff09;用于一次读取一组数据&#xff0c;即按数据块读写文件。fread&#xff08;&#xff09;的函数原型为&#xff1a; unsigned int fread(void *buffer ,unsigned int size,unsigned int count ,FILE *fp);…

raid5故障导致LeftHand存储崩溃的服务器数据恢复案例

HP-LeftHand存储简介&#xff1a; HP LeftHand存储支持RAID5、RAID6、RAID10磁盘阵列&#xff0c;支持卷快照&#xff0c;卷动态扩容等。 服务端&#xff1a; 客户端&#xff1a; LeftHand存储分为三个层级&#xff1a;物理磁盘、逻辑磁盘、逻辑卷。多个物理磁盘组成一个逻辑的…

pdf文档加水印怎么弄?用这款软件很方便

在工作中&#xff0c;我们经常需要将PDF文件发送给他人&#xff0c;但无法保证文件内容不被窃取&#xff0c;因此需要添加水印来保证文件的安全性。如果你不知道如何给PDF文件添加水印&#xff0c;以下两款软件可以帮助你轻松实现&#xff0c;一起来看看吧&#xff01; 方法一&…

火爆全网,自动化测试-Allure完美测试报告(详全)卷起来...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、allure简介 A…

网络协议与攻击模拟-17-DNS协议-报文格式

二、DNS 查询 客户机想要访问www.baidu.com&#xff0c;根据自己的 TCP / IP 参数&#xff0c;向自己的首选 DNS 服务器发送 DNS 请求 首选 DNS 收到客户机的请求后&#xff0c;会去查询自己的区域文件&#xff0c;找不到www.baidu.com的 IP 地址信息&#xff08;将请求转发到…

天池大赛中药说明书实体识别挑战冠军方案开源(二)部署运行实战 附详细操作说明

目录 Introduction 导言环境搭建环境 Github地址项目目录说明使用步骤下载预训练模型更改部分代码模型训练参数设置BERT-CRF模型训练BERT-SPAN模型训练BERT-MRC模型训练 运行训练预测复赛 test 文件 &#xff08;上述模型训练完成后&#xff09; 参考资料其它资料下载 Introduc…

Rdkit|化学指纹(fingerprint)

github:地址 文章目录 Rdkit|化学指纹&#xff08;fingerprint&#xff09;化学指纹&#xff08;fingerprint&#xff09;RDKFingerprintMorgan指纹提供的指纹信息存储在字典中 MACCS指纹AtomPair指纹TopologicalTorsion指纹参考 Rdkit|化学指纹&#xff08;fingerprint&#x…

使用楔形步进体模进行X射线骨密度测定

来源&#xff1a;投稿 作者&#xff1a;洪棋 编辑&#xff1a;学姐 骨密度(BMD)被广泛应用于骨折风险的预测和骨质疏松症的常规识别。双能x线骨密度仪(DXA)在临床上广泛用于测量脊柱、髋关节和前臂的骨密度(aBMD)。放射学骨密度测定法(Radiographic absorpometry, RA)是最早的骨…

最全Linux Shell详细教程

一、环境准备 我们在这里就在本地测试学习&#xff0c;因此我在这里先不建议大家这么着急去买服务器来学&#xff0c;这样比较费钱&#xff0c;等我们学好这些基础之后&#xff0c;再去上手服务器&#xff0c;那将是手到擒来。 本地学习工具&#xff1a;虚拟机 如果你没有该工…

ES6~ES13新特性(一)

1 ECMA新描述概念 2 let、const的使用 3 let、const和var区别 4 块级作用域的使用 5 模板字符串的详解 6 ES6函数的增强用法 一个执行上下文关联两个环境。词法环境和变量环境。 词法环境是由let和const创建&#xff1b;变量环境是由var创建的。 let-const的基本使用、不能…

网络安全现状,一个黑客真实的收入

前言 上次带大家了解了什么是黑客&#xff0c;黑客是干嘛的&#xff0c;今天就来看看黑客的收入和方向怎么样。 一个黑客年薪是多少呢&#xff1f; 外界普遍认为黑客是高收入群体&#xff0c;那么你想过黑客是怎么赚钱的吗&#xff1f;黑客分为白帽黑客和黑帽黑客&#xff0…

R语言实现SMOTE与SMOGN算法解决不平衡数据的回归问题

本文介绍基于R语言中的UBL包&#xff0c;读取.csv格式的Excel表格文件&#xff0c;实现SMOTE算法与SMOGN算法&#xff0c;对机器学习、深度学习回归中&#xff0c;训练数据集不平衡的情况加以解决的具体方法。 在之前的文章Python实现SMOGN算法解决不平衡数据的回归问题&#x…