0基础 三个月掌握C语言(14)-上

自定义类型

其实C语言有内置类型 也有自定义的类型

内置类型(C语言本身支持的现成的类型) 如:char short int long float double....

但仅仅有这些类型是不够的

比如说 我们想表示一个人(复杂对象)

对于人的描述那就很多了 比如:性别 年龄 身高........

所以C语言就有了自定义类型

自定义类型:结构体 

其实我们之前有说过结构体的一些知识点 现在我们再进行复习一遍 以加深印象

结构体回顾

结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

结构体类型的声明

例如描述一个学生:

我们这里定义了一个struct Stu的结构体类型 它的成员变量有三个 字符数组name 整形变量age 字符数组gender

注意:最后的分号不能丢

结构体变量的创建和初始化

结构的特殊声明

在声明结构的时候,可以不完全的声明。

⽐如:匿名结构体类型

这里我们在声明的时候 省略掉了结构体标签 即struct后面为空

正常是struct x   x为结构体标签

如何在上述代码的基础上 我们让

p=&x;

警告:

编译器会把上面的两个声明当成完全不同的两个类型 所以是非法的

匿名的结构体类型 如果没有对结构体类型重命名的话(typedef) 基本上只能使用一次

我们给匿名结构体类型重命名为X  然后定义了一个名为i的结构体变量

结构的⾃引⽤ 

在讲之前 我们还得先讲一下数据结构中的链表

当然  还是希望大家自行去搜索 因为这里我只能讲个大概

数据结构--其实是数据在内存中的存储和组织(各个数据之间的关系)的结构

那我们开始讲链表了

这里我们讲下链表

假设要在内存中存储1 2 3 4 5

我们当然可以用数组来存储(连续空间)---在数据结构也称为顺序表

但我们的链表可不是连续的哦

是靠各个结点(存储数值和下一结点的地址)连接起来

当然我们也需要有一个头结点来找到我们存储的数据

在最后一个结点存放地址的位置 我们置为空 说明之后无结点了

我们只要找到1就可以找到其余数

每个结点分成两部分(data域:存储数值   指针域:存储下一结点的地址)

指针域:使得当前结点可以指向下一结点

在结构体中包含⼀个类型为该结构本⾝的成员是否可以呢?

⽐如,定义⼀个链表的结点:

上述代码正确吗?如果正确,那 sizeof(struct Node) 是多少?

仔细分析,其实是不⾏的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤ ⼩就会⽆穷的⼤,是不合理的。想象成套娃一样

正确的⾃引⽤⽅式:

我们让next成为一个指针 指向下一个结构体的地址 这样就可以实现结构体间的连接

在结构体⾃引⽤使⽤的过程中,夹杂了 typedef 对匿名结构体类型重命名,也容易引⼊问题,看看

下⾯的代码,可⾏吗?

答案是不行的

这里的struct Node* next中的struct Node此时还不能省

我们是先定义结构体类型 再重命名 不能在定义结构体类型的时候 直接使用重命名后的名字

当然匿名的结构体类型是不能实现这种结构体自引用的效果的

正确形式应该为:

用typedef给结构体类型(struct Node)重命名为Node

当然下面代码也能实现重命名

结构体内存对⻬

我们已经掌握了结构体的基本使⽤了。

现在我们深⼊讨论⼀个问题:计算结构体的⼤⼩。

这也是⼀个特别热⻔的考点: 结构体内存对⻬

那我们先上一段代码来看看你是否能计算出来结构体大小

我刚开始看到这题的时候 我给出的答案是4  我当时认为结构体变量s初始化为数字0

以为这个0会赋给i  又因为i为int类型 所以得出结果为4

但是答案为12

对齐规则

⾸先得掌握结构体的对⻬规则:

1.结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处

//这里就意味着我们的c1存放在偏移量为0的地址处

2.其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。

//这里我们的i就从偏移量为4的地址起始向下取4个字节(4--7)

//我们的c2的对齐数为1  8是1的倍数 所以我们的c2存在偏移量为8的地址处

对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。

VS 中默认的值为 8

//这里i的大小为4字节  所以这里我们i的对齐数为4(4<8)

注意:Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的

整数倍。

//我们本题的最大对齐数为4  结构体的总大小为最大对齐数的整数倍

//结构体的总大小:9 10 11都不是4的整数倍 12是4的倍数 所以本题输出的是12(偏移量为0-11)

x表示空间被浪费了 不存放数据

4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

练习1

c1 c2分别放在偏移量为0 1的地址处

因为i的最大对齐数为4(在VS中 默认为8  这里4<8)

所以i占据偏移量为4--7的空间 结构体的总大小为4的倍数 刚好8是4的倍数

所以答案为8

练习2

double类型占8个字节--最大对齐数

所以其占据偏移量0-7的地址 c放在偏移量为8的地址处

i的对齐数为4 找到一个4的倍数 即为12  所以偏移量12-15的地址处存放i

16是最大对齐数8的倍数 所以16是最终结果

练习3

嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处

所以我们嵌套的s3(最大对齐数为8)从偏移量为8的位置向后占据16个字节

然后变量d的对齐数为8  刚好24是8的倍数  d便占据偏移量24-31的地址

刚好0-31  这里的结构体的整体大小为32 是最大对齐数8的倍数 所以结果为32

为什么存在内存对⻬?

⼤部分的参考资料都是这样说的:

1. 平台原因 (移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2. 性能原因:

数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。

总体来说:结构体的内存对⻬是拿空间来换取时间的做法。

举例:

假设我们给一个结构体

struct S

{

        char c;//1

        int i;//4

};

在这里我们应该需要8个字节来存放该结构体

假设我们32位系统每次读取4个字节

第一种情况 一次即可读取到i

第二种情况 则需要两次才能读取出来 第一次读取不全 第二次补全

那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:

让占⽤空间⼩的成员尽量集中在⼀起

修改默认对齐数

我们在上面知道了VS的默认对齐数为8

#pragma这个预处理指令 可以改变编译器的默认对齐数

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

结构体传参

我们的arr很大 所以我们的结构体s也很大

我们的函数print1 的tmp也会创建一个很大的空间 这会很浪费空间(但不会修改s)

所以不建议这样 我们现在给出另一种方式来传参

我们这里直接给print2传结构体s的地址 这就避免了空间的浪费(但可能改变s)

所以我们可以在这里用const限制

这里的原因是:

函数传参的时候 参数是需要压栈 会有时间和空间的系统开销

如果传递一个结构体对象的时候 结构体过大 参数压栈的系统开销比较大 所以会导致性能的下降

结论:结构体传参的时候 要传结构体的地址

结构体实现位段

结构体讲完就得讲讲结构体实现 位段 的能⼒。

什么是位段?

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

1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他类型。

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

⽐如:

位段的位其实是二进制位

在结构体S1   int类型占4个字节--32个bit位

假如我的_a是0或1  二进制表示即为00  01 两个bit位即可 所以32bit位会浪费

而我的位段S2  _a占2个bit位  _b占5个bit位  _c占10个bit位 _d占30个bit位

所以位段是专门用来节省内存的

我们知道上面的结构体S1占16个字节 那我们的位段S2占几个字节呢

结果为什么是8呢 我们在位段的内存分配来讲解

位段的内存分配

1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型

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

3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。

在第三点中 不同的编译器对位段的实现可能是不同的

现在我们再举个例子:

做这题前 我先告诉大家几个注意点

1.申请到的一块内存中 从左向右使用 还是从右向左使用 是不确定的

在VS中 是从右向左使用的

2.剩余的空间不足下一个成员使用的时候 是浪费呢 还是继续使用呢

那我们的各个数又是如何存储的呢

a二进制数为1010 但因为a只占3个bit位 存后三个 二进制数 则存的是010 当然当我们输出a的值发现a不是10 而是2 就是因为这个原因

我们调试一下 看下是否如此

位段的跨平台问题 

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

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

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

4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃剩余的位还是利⽤,这是不确定的。

总结:跟结构体相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

所以我们根据不同的平台写不同的代码--解决跨平台的问题

位段的应⽤

下图是⽹络协议中,IP数据报的格式,我们可以看到其中很多的属性只需要⼏个bit位就能描述,这⾥使⽤位段,能够实现想要的效果,也节省了空间,这样⽹络传输的数据报⼤⼩也会较⼩⼀些,对⽹络的畅通是有帮助的。

假如我们A要给B发送“呵呵”这条消息

我们现在用 送快递的方式来大概理解一下

我们知道在寄包裹时 需要在包裹上写 寄件人 收件人

这里的寄件人就如同源IP地址

收件人就如同目的IP地址

我们看到第一行4位版本号 假如我们用char(1个字节-8bit位)我们发现就浪费了一半的空间 这时候用位段就完美的节省了空间 而且我们看到它一共占20个字节 其实它对空间的把握特别好

位段使⽤的注意事项

位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。

所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员

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

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

相关文章

我们是如何测试人工智能的(八)包含大模型的企业级智能客服系统拆解与测试方法 -- 大模型 RAG

大模型的缺陷 -- 幻觉 接触过 GPT 这样的大模型产品的同学应该都知道大模型的强大之处&#xff0c; 很多人都应该调戏过 GPT&#xff0c;跟 GPT 聊很多的天。 作为一个面向大众的对话机器人&#xff0c;GPT 明显是鹤立鸡群&#xff0c;在世界范围内还没有看到有能跟 GPT 扳手腕…

开启Safari手势支持

在使用Safari 的时候&#xff0c;大家有没有觉得不支持手势使用起来不是很方便&#xff0c; 触摸板只支持少量简单的手势&#xff0c;如缩放&#xff0c;滚动等。如果使用鼠标的用户&#xff0c;则完全无法使用手势。经过折腾研究&#xff0c;使用CirMenu应用可以完美解决这个要…

C++第十一弹---类与对象(八)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、友元 1.1、友元函数 1.2、友元类 2、内部类 3、匿名对象 4、拷贝对象时的一些编译器优化 总结 1、友元 友元提供了一种突破封装的方式&a…

深度解析:Elasticsearch写入请求处理流程

版本 Elasticsearch 8.x 原文链接&#xff1a;https://mp.weixin.qq.com/s/hZ_ZOLFUoRuWyqp47hqCgQ 今天来看下 Elasticsearch 中的写入流程。 不想看过程可以直接跳转文章末尾查看总结部分。最后附上个人理解的一个图。 从我们发出写入请求&#xff0c;到 Elasticsearch 接收请…

单目深度估计基础理论和论文学习总结

单目深度估计基础理论和论文学习总结 一、背景知识&#xff1a; 三维刚体运动的数学表示&#xff1a;旋转平移矩阵、旋转向量、欧拉角、四元数、轴角模型、齐次坐标、各种变换等 照相机模型&#xff1a;单目/双目模型&#xff0c;单目中的世界坐标系/相机坐标系/图像坐标系的…

从零开始的 dbt 入门教程 (dbt cloud 自动化篇)

一、引 在前面的几篇文章中&#xff0c;我们从 dbt core 聊到了 dbt 项目工程化&#xff0c;我相信前几篇文章足够各位数据开发师从零快速入门 dbt 开发&#xff0c;那么到现在我们更迫切需要解决的是如何让数据更新做到定时化&#xff0c;毕竟作为开发我们肯定没有经历每天定…

【项目管理后台】Vue3+Ts+Sass实战框架搭建二

Vue3TsSass搭建 git cz的配置mock 数据配置viteMockServe 建立mock/user.ts文件夹测试一下mock是否配置成功 axios二次封装解决env报错问题&#xff0c;ImportMeta”上不存在属性“env” 统一管理相关接口新建api/index.js 路由的配置建立router/index.ts将路由进行集中封装&am…

GA遗传算法和ALNS算法的区别(我的APS项目七)

博主用最简单的方式告诉你遗传算法是什么&#xff0c;估计这是网上最简单的遗传算法入门教程了。首先我们先带入一个问题&#xff0c;我们要去9大城市旅游&#xff0c;想知道每个城市走一遍&#xff0c;总路程最短的出行顺序是什么&#xff1f; OK&#xff0c;题目我们已经明确…

Chrome 插件打包发布

插件打包发布 一、打包成 zip 包 最简单方便的一种其实就是打包成 zip 包&#xff0c;通过下载链接进行下载&#xff0c;在包里面通过设置版本号和数据库的版本号对比来提醒用户进行新包的下载。 二、发布到 Chrome 应用商店 1. 注册成为开发者 在发布到 chrome 应用商店之…

第八节:深入讲解SMB中的Http组件

一、概述 Http组作是SMB中的核心组件之一&#xff0c;在第七节中讲解了如何简洁的进行web程序部署和运行&#xff0c;这只是它的功能之一。在本节中&#xff0c;我们将介绍Http组件的重要属性。 二、请求头Request 1、支持方法 支持POST、GET、PUT、DELETE、OPTIONS等方法&a…

吴恩达深度学习笔记:神经网络的编程基础2.15-2.17

目录 第一门课&#xff1a;神经网络和深度学习 (Neural Networks and Deep Learning)第二周&#xff1a;神经网络的编程基础 (Basics of Neural Network programming)2.15 Python 中的广播&#xff08;Broadcasting in Python&#xff09;2.16 关于 python _ numpy 向量的说明&…

作为技术人员在日常工作中如何使用边界AICHAT工具

目录 1.1、解决日常问题1.2、编写日常程序1.3、优化日常工作中的代码1.4、边界AICHAT工具会员中心1.5、边界AICHAT工具普通用户的权益1.6、边界AICHAT工具超级永久会员的权益 有关边界AICHAT工具工具的介绍请参考之前的系列博文&#xff0c; 一款好用的AI工具——边界AICHAT&a…

【SpringSecurity】十六、OAuth2.0授权服务器、资源服务器的配置(理论部分)

文章目录 0、OAuth2服务端结构1、授权服务配置2、授权服务器 ⇒ 配置客户端详情3、授权服务器 ⇒ 管理令牌配置4、授权服务器&#xff1a;配置端点访问的安全约束5、资源服务器配置 相关&#x1f4d5;&#xff1a; 【OAuth2授权服务器配置完整Demo】 0、OAuth2服务端结构 OAu…

微服务(基础篇-003-Nacos集群搭建)

目录 Nacos集群搭建 1.集群结构图 2.搭建集群 2.1.初始化数据库 2.2.下载nacos 2.3.配置Nacos 2.4.启动 2.5.nginx反向代理 2.6.优化 视频地址&#xff1a; 06-Nacos配置管理-nacos集群搭建_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1LQ4y127n4?p29&…

【Linux文本处理三剑客-grep、awk、sed】

grep, awk, 和 sed 是三个强大的文本处理工具&#xff0c;在Linux系统中广泛使用。每个工具都有其独特的特性和用法&#xff0c;下面我会对每一个工具进行简要的介绍和说明。 1.grep grep 是一个用于模式搜索的工具&#xff0c;它可以在文件或者标准输入中搜索包含特定模式的…

国内ip地址随意更换的潜在风险和策略

在数字化时代&#xff0c;IP地址是互联网通信的基础&#xff0c;而国内IP地址的随意更换可能带来一系列安全风险和问题。虎观代理小二将探讨国内IP地址随意更换的潜在影响以及如何有效应对这一挑战。 1.什么是国内IP地址&#xff1f; 国内IP地址是指在国内分配和使用的IP地址&…

边缘计算基础介绍及AKamai-linode产品分析

1、背景 随着互联网的发展&#xff0c;我们进入了大数据时代&#xff0c;这个时代也是移动互联网的时代&#xff0c;而且这个时代&#xff0c;大量的线下服务走到线上&#xff0c;随之而来的&#xff0c;比如外卖、叫车……于是&#xff0c;有各种各样的 App 和设备在收集你的…

对尾递归的理解,有哪些应用场景

文章目录 一、递归二、尾递归二、应用场景参考文献 一、递归 递归&#xff08;英语&#xff1a;Recursion&#xff09; 在数学与计算机科学中&#xff0c;是指在函数的定义中使用函数自身的方法 在函数内部&#xff0c;可以调用其他函数。如果一个函数在内部调用自身本身&am…

电子电器架构 —— 诊断数据DTC具体故障

电子电器架构 —— 诊断数据DTC具体故障 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师 (Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶,喝完再挣…

qt5-入门-标签页部件QTabWidget-2-新增和删除标签页

参考&#xff1a; C GUI Programming with Qt 4, Second Edition 本地环境&#xff1a; win10专业版&#xff0c;64位&#xff0c;Qt5.12 上一篇&#xff1a; qt5-入门-标签页部件QTabWidget-1-CSDN博客 https://blog.csdn.net/pxy7896/article/details/136883359 目录 效果实…