C语言—指针(2)

目录

一、数组名的理解

二、使用指针访问数组

三、一维数组传参的本质

四、冒泡排序

五、二级指针

六、指针数组

七、字符指针变量

八、数组指针变量

(8.1)什么是数组指针变量

 (8.2)数组指针变量的初始化

九、二维数组传参的本质

十、函数指针变量

(10.1)函数指针变量的创建

(10.2)函数指针变量类型的使用

(10.3)typedef关键字

十一、函数指针数组


一、数组名的理解

通常情况下,我们使⽤ &arr[0] (随便写的一个数组名)的⽅式拿到数组第⼀个元素的地址,但是其实数组名本来就是地址,⽽且是数组⾸元素的地址。测试:

我们发现数组名和数组⾸元素的地址打印出的结果⼀模⼀样,数组名就是数组⾸元素(第⼀个元素)的地址。但是,有两种特殊情况,数组名不表示数组首元素的地址。

1、 sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节。
2、 &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)。

除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址。测试:

这里sizeof(arr)确实是整个数组的大小,但是 &arr 和数组首元素地址相同,这是为什么呢?其实,&arr 取出的的确是整个数组的地址,不过因为数组在内存中是连续存放的,只要拿到首元素地址就能找到其他元素,所以整个数组的地址就是数组首元素的地址。但是它们也是有区别的。

打印结果:(这里是十六进制,计算相差字节数需要转换为十进制)

这⾥我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1相差4个字节,这是因为&arr[0] 和 arr 都是⾸元素的地址,+1就是跳过⼀个元素。但是&arr 和 &arr+1相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组。

二、使用指针访问数组

使用指针访问数组有两种基本方式,第一种:

打印效果:

上述代码中,先使用指针记住数组首元素地址,在通过指针+i 使指针指向不同的数组元素,进而实现赋值和打印。

第二种:

打印效果:

这里是因为,数组名arr是数组⾸元素的地址,指针p也是数组首元素的地址,所以它们是等价的,那我们可以使⽤arr[i]访问数组的元素,就可以使用p[i]访问数组的元素。同时,这里将*(p+i)换成p[i]也是能够正常打印的,所以本质上p[i] 还等价于 *(p+i)。同理arr[i] 应该等价于 *(arr+i),数组元素的访问在编译器处理的时候,也是转换成⾸元素的地址+偏移量求出元素的地址,然后解引⽤来访问的。

三、一维数组传参的本质

我们知道 ,数组是可以传递给函数的,我们之前都是在函数外部计算数组的元素个数,那我们可以把数组传给⼀个函数后,在函数内部求数组的元素个数吗?

我们发现在函数内部是没有正确获得数组的元素个数的。

其实,数组名是数组⾸元素的地址;那么在数组传参的时候,传递的是数组名,也就是说数组传参本质上传递的是数组⾸元素的地址。所以函数形参的部分理论上应该使⽤指针变量来接收⾸元素的地址。那么在函数内部我们写sizeof(arr) 计算的是⼀个地址的⼤⼩(单位字节)⽽不是数组的⼤⼩(单位字节)。正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。

打印结果:

总结:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

四、冒泡排序

上述代码演示的是升序排列,冒泡排序的核⼼思想就是:两两相邻的元素进⾏⽐较。如图:

从图中可以看出,第一次内层循环遍历结束后,数组中最大的数被放置到数组末尾,所以第二次进入内层循环时,最后一个元素没有必要参与比较,第二次内层循环结束后,次大的数又被排好,所以每次都会有一个参与比较的数据中的最大值被排好,所以每次内层循环都可以少排一个数据,所以内层循环的条件是sz-i-1,第一次 i 等于0,sz个数据需要比较sz - 1次,之后每次 i +1,内层循环次数每次就减少一次。又因为每次都是排好一个数据,一共sz个数据,当sz - 1个都排好后,最后一个就不需要比较了,所以外层循环需要sz - 1次。

这个代码还可以改进,图中演示的是最坏的情况,不一定所有情况都要排sz - 1次。如图:

这种情况一次就排好了。所以代码可以改进为:

五、二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪⾥呢?存放指针变量的就叫⼆级指针 。

图中的ppa就是二级指针,int * 代表它指向的对象类型是整型指针,后面的第二个 * 代表它自己是指针。对于二级指针而言,*ppa 通过对ppa中的地址进⾏解引⽤,这样找到的是 pa , *ppa 其实访问的就是 pa ,**ppa 先通过 *ppa 找到 pa ,然后对 pa 进⾏解引⽤操作: *pa ,那找到的就是 a 

六、指针数组

首先我们要知道,指针数组是指针还是数组?我们类⽐⼀下,整型数组,是存放整型的数组,字符数组是存放字符的数组。所以指针数组,是存放指针的数组。指针数组的每个元素都是⽤来存放地址(指针)的。如图:

指针数组的每个元素是地址,⼜可以指向⼀块区域。例如:

打印效果:

原理图:

parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数组中的元素。

七、字符指针变量

一般情况下,我们可以这样使用字符指针:

但是还有一种使用方式:

这种方式的本质是把字符串 hello bit. 的⾸字符的地址放到了pstr中。

我们可以看这样一道题:

结果:

解释:上述代码中,str1和str2是两个数组,它们会分别在内存中开辟自己的空间,存放相同的字符串,前面说过,数组名是数组首元素的地址,而str1和str2都有各自的空间,只是空间里存的内容一样而已,地址并不相同,所以第一个打印出来的是 not same,而str3和str4指向的是同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域,当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str3和str4打印出的是 same。

八、数组指针变量

(8.1)什么是数组指针变量

类比以前学过的内容:整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针;浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量。

数组指针和指针数组的基本格式很像,我们要区分开,首先我们需要知道 [ ] 的优先级要⾼于 * 号的,所以数组指针要加上(),例如上图:p2先和*结合,表明p2是⼀个指针变量,然后指向的是⼀个⼤⼩为10个整型的数组。所以p2是⼀个指针,指向⼀个数组,叫数组指针。p1没有(),先和 [ ] 结合,成为一个数组,再和 * 结合,表明数组中存放的是整型指针,所以p1是指针数组。

 (8.2)数组指针变量的初始化

数组指针变量是⽤来存放数组地址的,要想获得数组的地址,就要用到前面说的:&数组名

例如:

通过调试我们可以看到 &arr 和 p 的类型是完全⼀致的。

数组指针类型解析:

九、二维数组传参的本质

一般情况下, 二维数组传参,形参也写成⼆维数组的形式。

二维数组传参还有另一种写法,不过我们要再次理解⼀下⼆维数组,⼆维数组起始可以看做是每个元素是⼀维数组的数组,也就是⼆维数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。如图:

所以,根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀维数组的地址。根据上⾯的例⼦,第⼀⾏的⼀维数组的类型就是 int [3] ,所以第⼀⾏的地址的类型就是数组指针类型 int(*)[3] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:

p 是数组指针,p + i 每次会跳过二维数组的一行,*(p+i)是得到当前数组指针指向的一维数组,*(p+i)+j是让指针移动到当前数组指针指向的这一行的一维数组的第 j 个元素,再解引用就能拿到这个元素了。

总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

十、函数指针变量

(10.1)函数指针变量的创建

函数指针变量是⽤来存放函数地址的,未来通过地址能够调⽤函数的。如何取出函数地址呢?如图:

从上述代码中可以看出:函数名就是函数的地址,当然也可以通过 &函数名 的⽅式获得函数的地址。如果我们要将函数的地址存放起来,就得创建函数指针变量,函数指针变量的写法其实和数组指针⾮常类似。如下:

函数指针类型解析:

(10.2)函数指针变量类型的使用

可以通过函数指针调⽤指针指向的函数。例如:

这里可以通过解引用来找到函数指针指向的函数并进行调用,也可以直接用指针进行调用,因为函数名就是函数的地址,函数指针也是函数的地址,所以函数指针等价于函数名,可以直接用函数指针对函数进行调用。

(10.3)typedef关键字

typedef 是⽤来类型重命名的,可以将复杂的类型简单化。⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤:

指针类型也是可以重命名的,⽐如,将 int* 重命名为 ptr_t ,这样写:

但是对于数组指针和函数指针稍微有点区别:
⽐如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

函数指针类型的重命名也是⼀样的,⽐如,将 void(*)(int) 类型重命名为 pfun_t ,就可以这样写:

十一、函数指针数组

基本格式:

解释:parr1 先和 [ ] 结合,说明 parr1是数组,数组的内容是int (*)() 类型的函数指针。

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

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

相关文章

【工业机器人】工业异常检测大模型AnomalyGPT

AnomalyGPT 工业异常检测视觉大模型AnomalyGPT AnomalyGPT: Detecting Industrial Anomalies using Large Vision-Language Models AnomalyGPT是一种基于大视觉语言模型(LVLM)的新型工业异常检测(IAD)方法。它利用LVLM的能力来理…

企业级WEB应用服务器TOMCAT

目录 一、WEB技术 1.1 HTTP协议和B/S 结构 1.2 前端三大核心技术 1.2.1 HTML 1.2.2 CSS(Cascading Style Sheets)层叠样式表 二、WEB框架 2.1 web资源和访问 2.2 后台应用架构 2.2.1 单体架构 2.2.2 微服务 2.2.3 单体架构和微服务比较 三、t…

springboot社区疫情返乡管控系统--论文源码调试讲解

第2章 开发环境与技术 本章节对开发社区疫情返乡管控系统管理系统需要搭建的开发环境,还有社区疫情返乡管控系统管理系统开发中使用的编程技术等进行阐述。 2.1 MySQL数据库 MySQL是一种具有安全系数、安全系数、混合开发性、高效化等特征的轻量关联数据库智能管…

最新动态鲨鱼导航网引导页html源码

源码介绍 最新动态鲨鱼导航网引导页html源码 源码由HTMLCSSJS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果,也可以上传到服务器里面,重定向这个界面 https://download.csdn.net/download/h…

鸿蒙HarmonyOS开发:用户通知服务Noification的详细使用指南

文章目录 一、Notification Kit简介二、能力范围三、业务流程四、通知样式:五、约束限制六、开发步骤6.1、导入模块。6.2、构造NotificationRequest对象,并发布通知。6.2.1、普通文本类型。6.2.2、长文本类型。6.2.3、多行文本类型。 6.3、为通知添加行为…

反射异常捕获 | InvocationTargetException 要用e.getCause()打印才能看到具体异常

背景:线上某段和反射相关的代码报错了,但是异常信息打印只看到了 InvocationTargetException,没打印具体的异常。就像这样:java.lang.reflect.InvocationTargetException: null 查阅资料后发现要用e.getCause()才能打印具体异常&a…

赚大钱和赚小钱,哪个更累?

最近一直在质疑我在做的项目,虽然有同行做到了很好的成绩,但是我还是质疑。 因为一直在赚小钱,接触到的也是新手、底层客户、墨迹客户。 越是钱少的生意,越不好做,客户越挑剔。 而且赚小钱会消磨人的心智。 前几年…

实现 FastCGI

CGI的由来: 最早的 Web 服务器只能简单地响应浏览器发来的 HTTP 请求,并将存储在服务器上的 HTML 文件返回给浏 览器,也就是静态 html 文件,但是后期随着网站功能增多网站开发也越来越复杂,以至于出现动态技 术&…

【ACM出版,高录用EI快检索】第七届计算机信息科学与人工智能国际学术会议(CISAI 2024,9月6-8)

第七届计算机信息科学与人工智能国际学术会议(CISAI 2024) 将于2024年09月6-8日在中国浙江-绍兴举行。 计算机信息科学与人工智能国际学术会议的主题主要围绕“信息科学”与“人工智能”的最新研究展开,旨在荟聚世界各地该领域的专家、学者、研究人员及相关从业人员…

图的应用

一、最小生成树 1)Prim算法(加点) 2)Kruskal算法(加边) 二、最短路径 1)Dijkstra算法 2)Floyd算法 三、拓扑排序 1)AOV 拓扑序列不唯一 2)AOE(关键路径&#…

CMOS 逆变器的功耗

CMOS 反相器的发展为集成电路提供了基本功能,是技术史上的一个转折点。该逻辑电路突出了使 CMOS 非常适合高密度、高性能数字系统的电气特性。 CMOS 的优势之一是其效率。CMOS 逻辑仅在改变状态时才需要电流——仅维持逻辑高或逻辑低电压的 CMOS 电路消耗的功率非常…

校园一卡通_q7e7o

TOC springboot576校园一卡通_q7e7o--论文 第一章 概述 1.1 研究背景 近些年,随着中国经济发展,人民的生活质量逐渐提高,对网络的依赖性越来越高,通过网络处理的事务越来越多。随着校园一卡通的常态化,如果依然采用…

IO进程(6)

目录 1.进程间通信 1.1无名管道 1.1.1读写特性 1.1.2函数 1.2有名管道 1.2.1函数接口 ​​​​​​​​​​​​​​1.2.2读写特性 2.信号 2.1信号的概念 ​​​​​​​​​​​​​​2.2信号的分类 ​​​​​​​​​​​​​​2.3信号的处理方式 ​​​​​​​2.4信号产生…

Tensorflow 2.16.0+在PyCharm中找不到keras的报错解决

在PyCharm(2024.2版本)中,直接使用from tensorflow import keras会提示“Cannot find reference ‘keras’ in ‘init.py’ ”,找不到keras,如下图所示。 查阅相关资料,可以发现在tf2.16之后,默认的keras后端升级为了…

【Git】常见命令的使用

Git 介绍流程安装常见命令本地仓与远程仓关联 介绍 Git、Svn:版本控制器(用于多人团队协作) Svn:集中式版本控制器;版本库集中放在中央服务器,操作非常简单,鼠标右键提交、新增、下载 Git&…

C语言小tip之整型提升

今天让我们来学习一下C语言中的一个小知识点-----整型提升 什么叫整型提升呢? C语言中整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。​为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型&a…

基于机器学习的二手房房价数据分析与价格预测模型

有需要本项目的可以私信博主,提供远程部署讲解 本研究聚焦重庆二手房市场,通过创新的数据采集和分析方法,深入探讨影响房价的关键因素,并开发了预测模型。 我们首先利用Python编写的爬虫程序,巧妙规避了链家网站的反…

环境配置1-MobaXterm服务器中Anaconda、Pytorch的安装

①登录 Login as 输入密码时密码不显示,正常输入即可 ②进入指定的下载目录 出现类似界面后,键盘操作Ctrlc即可进行输入 cd / …….(要下载到的目录名称)/ Anaconda的安装 ①输入wget https://repo.anaconda.com/archive/Anaconda3-2022.10-Linux…

基于Java的小区物业管理系统设计与实现

TOC springboot596基于Java的小区物业管理系统设计与实现--论文 研究背景 小区物业管理系统主要通过计算机网络,对小区物业管理系统所需的信息进行统一管理,方便用户随时随地进行增添、修改、查询、删除各类信息。本系统极大的促进了系统与数据库管理…

【Kubernetes】k8s集群之包管理器Helm

目录 一.Helm概述 1.Helm的简介 2.Helm的三个重要概念 3.Helm2与Helm3的的区别 二.Helm 部署 1.安装 helm 2.使用 helm 安装 Chart 3.Helm 自定义模板 4.Helm 仓库 每个成功的软件平台都有一个优秀的打包系统,比如Debian、Ubuntu 的 apt,RedH…