保姆级的指针详解(超详细)

news2024/10/6 2:28:46

目录

一.内存和地址 

1.初识指针

2.如何理解编址

二. 指针变量

三.指针的解引用操作符

1.指针变量的大小

四.指针变量类型的意义

五.指针的运算

1.指针加减整数

2.指针减指针

3.野指针

3.1指针未初始化

3.2指针越界访问

3.3指针指向的空间被提前释放

3.4如何规避野指针

六.void* 指针和const修饰指针

6.1void

6.2const

七.传值调用和传址调用

八.指针比较和二级指针

8.1指针比较

8.2二级指针

九.字符指针

十.指针数组

10.1指针数组模拟⼆维数组

十一.数组指针

11.1定义

11.2再次讨论数组名

11.3⼆维数组传参的本质

十二.函数指针

12.1函数指针定义

12.2函数指针变量的使用

12.3怪题

12.4回调函数

十三.函数指针数组

十四.指向函数指针数组的指

十五.qsort的实现

一.内存和地址

1.初识指针

在学习指针之前我们先要明白指针到底是什么,指针就是用来访问内存的,每一个单位内存都会占一个字节,也就是八个比特位,每一个内存单号有一个编号,就是地址,这样可以让CPU快速地找到他,我们可以把内存比做成房子,内存单号就是每一个住户,那么地址也就是你的门牌号了。所以我们可以理解为:内存单元的编号 == 地址 == 指针。在C语言中地址的新名字就可以把它看作成指针。

2.如何理解编址

CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节 很多,所以需要给内存进行编址,那么到底是如何编址的呢?

首先必须理解计算机内是有很多硬件单元,这些硬件单元他们是相互工作的,他们会进行数据传递相互的,我们可以来看一下这个图片图片中,一共有三个线地址,总线数据总线和控制总线。比如说,我要从内存中读取一个信息,这个读的指令就是通过控制总线内存向CPU传递的,然后CPU通过地址总线向找到内存所开辟的空间,然后内存再用数据总线传给CPU。不过,我们今天只关心组线,叫做地址总线。其实所谓硬件编制还跟你是几位机器有关,如果你是32位机器,那么你就有32位地址线,64位机器则64位地址总线,一根机器含有两个态0.1,所以说32根地址线就可以表示2的32次方含义,每种含义都可以表示一个地址.

二. 指针变量

所以说指针到底是什么呢?他就是地址,那么指针变量,他就是存放指针的变量,比如说整形变量int a=4;它可以用来存放一个整型,反之指针变量也是可以用来存放指针也就是地址。我们先来看一个最简单的指针变量。

这就是最简单的一个指针的创建,然后我们把它拆分开来看就可以了。注意指针变量是用来存放地址的,也就是存放变量a的

所以是指针他也是分类型的,如果有⼀个char类型的变量ch,ch的地址,要放在char*的指针变量种去。

三.指针的解引用操作符

那么这个存起来的地址到底有什么用呢?肯定我们将地址保存起来,未来是要使用的,那怎么使用呢? 在现实生活中,我们使用地址要找到⼀个房间,在房间里可以拿去或者存放物品。 C语言中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这里必须学习⼀个操作符叫解引用操作符(*)。

*pa 的意思就是通过pa中存放的地址,找到指向的空间, *pa其实就是a变量了;所以*pa = 0,这个操作符是把a改成了0.其实可以这样看*可以与&操作抵消因为pa=&a所以*pa=*&a=a'。

1.指针变量的大小

所以说指针变量的大小到底是什么呢?我们可以这样来推理,指针变量是用来存放地址的那么地址是怎么产生的呢?地址肯定是由地址线产生的,以32位机器为模板32位机器就是32根地址线,就是32个比特位那么32个比特位置四个字节,所以说指针变量大小就是地址的大小那么地址大小就和机器的这个位数是有关系的,你是64位,那你就是八个字节,注意类型是无关的

四.指针变量类型的意义

现在有一个令人疑惑的问题,就是这个指针变量它的存在到底有什么意义?指针变量的大小和类型无关,只要是指针变量,在同一个平台下,大小都是⼀样的,为什么还要有各 种各样的指针类型呢?我一个指针变量让他有这么多类型是干什么的?或者是他到底有什么用?

调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0。所以说,指针类型决定了指针进行节,引用操作的时候能够访问空间的大小

五.指针的运算

1.指针加减整数

用数组来举例子,因为数组在内存中是连续存放的,只到第一个元素的地址就能找到后面的所有,所以说数组的打印也可以用指针加减整数来去实现。

注意:这里有一个重要的思想,*(p+i)其实就等于arr[i],又因为p=arr所以arr[i]=p[i]  , *(p+i)=p[i]

只是一个很重要的思想,很多题目都会用到。

2.指针减指针

这里就是运用了指针减指针,注意不同类型的指针变量是不可以相减的,其实相同类型的指针相减可以看作这两个数组下标之间包含了几个元素,一般都是用大减小。

3.野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。野指针是非常危险的,就比如说野狗,如果没有人管的话,你怕不怕。所以说常见的野指针类型,我们是必须要知道的.

3.1指针未初始化

局部变量指针未初始化,默认为随机值,不知道初始化什么的时候,我们可以把它定做成一个空指针。

3.2指针越界访问

当指针指向的范围超出数组arr的范围时,p就是野指针。

3.3指针指向的空间被提前释放

因为出了test函数结束的时候,a的空间就会销毁,所以说访问的空间不再是当前程序的。所以地址找不到a,就会形成野指针。

3.4如何规避野指针

1.如果不知道指针应该指向哪⾥,可以给指针赋值NULL.

2.⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。

3.避免返回局部变量的地址

六.void* 指针和const修饰指针

6.1void

在指针类型中有⼀种特殊的类型是 void* 类型的,泛型指针),这种类型的指针可以用来接受任意类型地址。但是也有局限性,void* 类型的指针不能直接进行指针的+-整数和解引用的运算

6.2const

代表他有常属性是无法修改的,他修饰指针变量时,分为在指针变量的左侧和在指针变量的右侧这两种情况。

const在修饰指针变量的时候放在*右边,const限制的是指针变量本身,不能再指向别的变量,但是可以通过指针变量修改指向的内容。

放在右边的时候,限制的是指针指向的内容,不能通过指针来修改指向的内容可以修改指针变量本身的,只也就是修改指针变量的指向。

七.传值调用和传址调用

存在传值调用和传址调用形式,也就是在有些问题中非用指针不可,所以我们才会来学习指针。比如说,写一个函数,交换两个整型变量的值。

void Swap1(int x, int y)
{
    int tmp = x;
    x = y;
    y = tmp;
}
 

当Swap1函数调用结束后回到main函数,a和b的没法交换。Swap1函数在使用的时候,是把变量本身直接传递给了函数,这种调用函数的方式我们之前在函数的时候就知道了,这种就叫传值调用。

void Swap2(int* px, int* py)
{
    int tmp = 0;
    tmp = *px;
    *px = *py;
    *py = tmp;
}
我们可以看到实现成Swap2的方式,顺利完成了任务,这里调用Swap2函数的时候是将变量的地址传递给了函数,这种函数调用方式叫:传址调用。

所以未来函数中只是需要主调函数中的变量值来实现计,就需要传值调用

如果函数内部要修改 主调函数中的变量的值,就需要传址调用 

八.指针比较和二级指针

8.1指针比较

注意:C语言语法规定,只允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较

8.2二级指针

指针变量也是变量是变量就地址,那么指针变量的地址存放在哪儿呢?其实二级指针就是用来存放指针变量的地址的。所以说,三级指针四级指针也很容易解释了,三级指针就是存放二级指针地址的四级指针就是存放三级指针地址的。

总之还可以这么理解

九.字符指针

在指针的类型中,我们知道一种指针类型叫做字符指针char*,我们知道他的使用方式就可以了。

这里是把一个字符串全部放进去了,还是只放了首字母呢?注意,他是一个常量字符串,答案是他放的是首字母把常量字符串的首字母放到了变量中。

十.指针数组

指针数组,首先他是一个数组,是用来存指针的,后面还会出现数组指针,函数指针,函数指针数组,那这些东西该如何去判断呢,我们可以这样去看找主语,比如说好大儿好大儿,他是儿子,我们只要看后面的那个主语是谁就可以判断他是谁了。

10.1指针数组模拟⼆维数组

parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数 组中的元素。注意这里的parr[i][j]可以写成*(parr[i]+j)==*(*(pi)+j)。由直接的思想可以得出。

十一.数组指针

11.1定义

整形指针是用来存放整形地址的指针,字符指针是用来存放字符地址的指针,所以说数组指针就是用来存放数组地址的指针。

     int (p指向的数组的元素类型)(*p1)(变量名)[10] (指向数组的元素个数)  =&arr;         

11.2再次讨论数组名

数组名通常表示的都是首元素的地址,但是有两个意外,1.sizeof(数组名)这里数组名表示整个数组,计算的是整个数组大小。2.&数组名,这里的数组名表示的依然是整个数字,所以取地址取出的是整个数字的地址。注意:数组传参的本质是首元素的地址,所以形参访问的数组和实参的数组是同一个数组

11.3⼆维数组传参的本质

有了数组指针的理解,我们就能够讲⼀下⼆维数组传参的本质了。二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址。前面也讲过怎么实现二维数组这里就不展示代码了,主要还是讲解一个传递参数方式,我们用函数来打印二位数组的话肯定就需要传递参数。有两种传参的形式

1.void test(int (*p)[5], int r, int c)

2.void test(int a[3][5], int r, int c)

为什么这样可以呢?因为二维数组的首元素是他第一行,第一行的地址就是一个意位数组的地址。所以说我需要他的一个首元素,那么首元素就很好说了,我要么就直接打印他的数组名,要么就用数组指针,所以就会有这两种方法。所以说⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

十二.函数指针

12.1函数指针定义

就是指向函数的一个指针,对于函数来说,取地址函数名和函数名都是函数的地址。他的定义方法是(返回类型) (*p) (参数)。

定义:   int           (*pf3)     (int x, int y)

12.2函数指针变量的使用

可以直接通过指针来进入函数,为什么两种写法都可以,因为前面讲过了对于函数来说,取地址函数名和函数名都是函数的地址

12.3怪题

1          (*       (void    (*) ()    )   0)   ()   ;是什么意思?

题目来自于《c语言陷阱与缺陷》这句代码表达了什么意思直接看的话,是有点复杂,所以我们把他拆分下来,先看里面的void(*) ()这是一个函数指针类型,拿出去,还剩一个(*0)这是一个指针变量,表示0处的地址,所以

以上代码是一次函数的调用调用的是0作为地址处的函数,把0强制类型转化为一个没有参数返回类型,是void的函数地址,在调用0地址处的这个函数。

2            void         (*signal    (int , void   (*)  (int)  ))   (int)

比较第一题要更加复杂了,我们还是可以拆分来看,先看里面的signal(int,void(*(int)这是一个函数指针类型,拿去后,还剩一个void(*)(int)这也是一个函数指针类型,

以上代码是一次函数的声明,声明的signal函数的第一个数参数的类型是int,第二个参数的类型是函数指针,该指针指向的函数参数是int,返回类型是void,signal函数的返回类型,也是一个函数指针的函数,该指针指向的函数参数是int,返回类型是void。

12.4回调函数

就是一个通过函数指针调用的函数,如果你把函数指针(地址)作为参数传给了另一个函数,当这个指针被用来调用其所指向的函数的时候就叫做回调函数。

十三.函数指针数组

数组是一个存放相同类型数据的存储空间,那么如果我要把函数的地址存到一个数组中,这些数字该如何定义呢?这里就要用到函数指针数组了,首先他肯定是一个数组,我们用函数指针的结构再去套上数组就行了。

                               int (*arr[4]) (int,int) ={add,sub,mul,div};

运用了就是可以用它来定义一个计算机把他的计算机方法的函数全部定义在这个数组里

十四.指向函数指针数组的指针

因为我的能力也有限,在此只能补充一个定义,首先他肯定是指针,然后他是指向函数指针数组的

                                     int (*(*arr[4]) )(int,int)=&arr;

十五.qsort的实现

以前写过一篇就直接放在这里了用c语言自己实现qsort和冒泡排序-CSDN博客

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

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

相关文章

05:容器镜像技术揭秘|发布容器服务器|私有镜像仓库

容器镜像技术揭秘|发布容器服务器|私有镜像仓库 创建镜像使用commit方法创建自定义镜像。Dockerfile打包镜像创建apache服务镜像制作 php 镜像 微服务架构创建nginx镜像 发布服务通过映射端口发布服务容器共享卷 docker私有仓库 创建镜像 使用commit方法…

Mac用Crossover玩《幻兽帕鲁》手柄不能用怎么办? Mac电脑玩《幻兽帕鲁》怎么连接手柄? 幻兽帕鲁玩家超1900万

2024年首款爆火Steam平台的游戏《幻兽帕鲁》,在使用Crossover后可以用Mac系统玩了,很多玩家喜欢通过手柄玩游戏,它拥有很好的握持体验,长时间玩也不会很累,所以很多《幻兽帕鲁》玩家都喜欢用手柄来操作,很多…

idea 中 tomcat 乱码问题修复

之前是修改 Tomcat 目录下 conf/logging.properties 的配置,将 UTF-8 修改为 GBK,现在发现不用这样修改了。只需要修改 IDEA 中 Tomcat 的配置就可以了。 修改IDEA中Tomcat的配置:添加-Dfile.encodingUTF-8 本文结束

大路灯有必要买吗?五款年度好用大路灯推荐

随着人们生活水平上升,对健康的关注度也不断提高,护眼灯的需求也越来越多。而护眼落地灯作为一种新型的照明产品,具有独特的优点。护眼落地灯采用柔和的自然光源,能有效减少眼睛疲劳和视力损伤,提高工作和学习的效率。…

消息中间件特性

一:消息队列的主要作用是什么? 1.消息队列的特性: 业务无关,一个具有普适性质的消息队列组件不需要考虑上层的业务模型,只做好消息的分发就可以了,上层业务的不同模块反而需要依赖消息队列所定义的规范进行…

亚信安全助力宁夏首个人工智能数据中心建成 铺设绿色算力安全底座

近日,由宁夏西云算力科技有限公司倾力打造,亚信安全科技股份有限公司(股票代码:688225)全力支撑,总投资达数十亿元人民币的宁夏智算中心项目,其一期工程——宁夏首个采用全自然风冷技术的30KW机…

软考高项十大管理49个过程记忆口诀

一、十大管理口诀 口诀:范进整狗子,成人风采干 内容:范围管理、进度管理、整合管理、沟通管理、质量管理、成本管理、资源管理、风险管理、采购管理、干系人管理 二、49个过程口诀 1、整合管理 口诀:按章程计划指导知识、监控…

Java打印图形 九九乘法表

目录 双重循环九九乘法表打印长方形打印平行四边形打印三角形打印菱形打印空心菱形 三重循坏百钱买百鸡 双重循环 九九乘法表 在Java中,你可以使用嵌套的for循环来打印九九乘法表。以下是一个简单的示例: public class Main {public static void main…

wordpress找不回密码怎么办?4种方法设置新密码

有些WordPress站长太久不登录后台了,所以就忘记了管理员登录密码,这种情况我们应该怎么找回密码呢?或者设置一个新密码呢?下面boke112百科就跟大家分享4种方法设置WordPress新密码。 方法一、登录页面的“忘记密码?”…

React + react-device-detect 实现设备特定的渲染

当构建响应式网页应用时,了解用户正在使用的设备类型(如手机、平板或桌面)可以帮助我们提供更优化的用户体验。本文将介绍如何在 React 项目中使用 react-device-detect 库来检测设备类型,并根据不同的设备显示不同的组件或样式。…

用Python处理TDC激光测距数据并绘制为图片

用Python处理TDC激光测距数据并绘制为图片 说明一、定义全局变量变二、主函数入口三、处理原始文件数据四、将数据叠加统计生成图片五、额外的辅助函数六、将数据进行各种形式统计叠加七、原始数据形式八、 测试结果 说明 1. 主要是将TDC激光测距数据进行统计叠加并绘制为图片…

网络原理-TCP/IP(3) - 三次握手超详解析

TCP协议 连接管理 TCP的连接是虚拟的,抽象的,目的是让通信双方保存对方信息.在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接. 之前我们在网络编程中的 socket new Socket(ip, port); 这个操作就是建立连接.而这个操作知识调用了socket的api,真正建立的过程,是在…

FW如何区别 PAW3212DB-TJDT 和 PAW3220DB-TJDL/TJDR/TJDS

PAW3212DB-TJDT 和 PAW3220DB-TJDL/TJDR/TJDS 的引脚功能定义是一样的,只是封装有一点不一样。PAW3212DB-TJDT是圆形火山口,配的是圆孔透镜,PAW3220DB-TJDL/TJDR/TJDS是方形火山口,配的是方孔透镜。 PAW3212DB-TJDT 和 PAW3220DB-…

杂题——试题-算法训练-P0602

分析: 把要重排序的数字转成数组对数组进行排序,从小到大排序数组转成字符串,字符串转成数字,得到最小数再把最小数的字符串反转,得到最大数注意: 在java语言中,如果使用Arrays.toString(digits…

C++之平衡二叉搜索树查找

个人主页:[PingdiGuo_guo] 收录专栏:[C干货专栏] 大家好,我是PingdiGuo,今天我们来学习平衡二叉搜索树查找。 目录 1.什么是二叉树 2.什么是二叉搜索树 3.什么是平衡二叉搜索树查找 4.如何使用平衡二叉搜索树查找 5.平衡二叉…

C#,阿格里数(Ugly Number)的多种算法与源代码

1 丑数,阿格里数 阿格里数,即丑数(Ugly Number)、逊数(Humble Number)。 一般而言:把只包含质因子2,3和5的数称作丑数(Ugly Number)。例如6、8都是丑数&…

基于PSO-BP神经网络的风电功率MATLAB预测程序

微❤关注“电气仔推送”获得资料(专享优惠) 参考文献 基于风电场运行特性的风电功率预测及应用分析——倪巡天 资源简介 由于自然风具有一定的随机性、不确定性与波动性,这将会使风电场的功率预测受到一定程度的影响,它们之间…

FPGA高端项目:Xilinx Zynq7020系列FPGA 多路视频缩放拼接 工程解决方案 提供4套工程源码+技术支持

目录 1、前言版本更新说明给读者的一封信FPGA就业高端项目培训计划免责声明 2、相关方案推荐我这里已有的FPGA图像缩放方案我已有的FPGA视频拼接叠加融合方案本方案的Xilinx Kintex7系列FPGA上的ov5640版本本方案的Xilinx Kintex7系列FPGA上的HDMI版本本方案的Xilinx Artix7系列…

探索设计模式的魅力:从单一继承到组合模式-软件设计的演变与未来

设计模式专栏:http://t.csdnimg.cn/nolNS 在面对层次结构和树状数据结构的软件设计任务时,我们如何优雅地处理单个对象与组合对象的一致性问题?组合模式(Composite Pattern)为此提供了一种简洁高效的解决方案。通过本…

C++类与对象:默认成员函数

文章目录 1.类的6个默认成员函数2.构造函数3.析构函数4. 拷贝构造函数5.赋值运算符和运算符重载6.日期类实现7.const成员8.重载流插入<< &#xff0c;流提取>>1.流插入2.流提取 9.取地址及const取地址操作符重载 1.类的6个默认成员函数 空类:也就是什么成员都没有的…