当前位置: 首页 > news >正文

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

目录

一.内存和地址 

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博客

相关文章:

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

目录 一.内存和地址  1.初识指针 2.如何理解编址 二. 指针变量 三.指针的解引用操作符 1.指针变量的大小 四.指针变量类型的意义 五.指针的运算 1.指针加减整数 2.指针减指针 3.野指针 3.1指针未初始化 3.2指针越界访问 3.3指针指向的空间被提前释放 3.4如何规…...

R-YOLO

Abstract 提出了一个框架,名为R-YOLO,不需要在恶劣天气下进行注释。考虑到正常天气图像和不利天气图像之间的分布差距,我们的框架由图像翻译网络(QTNet)和特征校准网络(FCNet)组成,…...

Qt无边框窗口拖拽和阴影

先看下效果: 说明 自定义窗口控件的无边框,窗口事件由于没有系统自带边框,无法实现拖拽拉伸等事件的处理,一种方法就是重新重写主窗口的鼠标事件,一种时通过nativeEvent事件处理。重写事件相对繁琐,我们这里推荐nativeEvent处理。注意后续我们在做win平…...

ES6 Proxy详解

文章目录 概述Proxy 实例的方法get(target, propKey, receiver)set(target, propKey, value, receiver)has(target, propKey)deleteProperty(target, propKey)defineProperty(target, propKey, propDesc)getOwnPropertyDescriptor(target, propKey)getPrototypeOf(target)setPr…...

Prompt Learning 的几个重点paper

Prefix Tuning: Prefix-Tuning: Optimizing Continuous Prompts for Generation 在输入token之前构造一段任务相关的virtual tokens作为Prefix,然后训练的时候只更新Prefix部分的参数,PLM中的其他参数固定。针对自回归架构模型:在句子前面添…...

中科大计网学习记录笔记(三):接入网和物理媒体

前言: 学习视频:中科大郑烇、杨坚全套《计算机网络(自顶向下方法 第7版,James F.Kurose,Keith W.Ross)》课程 该视频是B站非常著名的计网学习视频,但相信很多朋友和我一样在听完前面的部分发现信…...

设计模式:工厂方法模式

工厂模式属于创建型模式,也被称为多态工厂模式,它在创建对象时提供了一种封装机制,将实际创建对象的代码与使用代码分离,有子类决定要实例化的产品是哪一个,把产品的实例化推迟到子类。 使用场景 重复代码 : 创建对象…...

HTML 相关知识点记录

<div> </div> DIV标签详细介绍-CSDN博客 div 是 division 的简写&#xff0c;division 意为分割、区域、分组。比方说&#xff0c;当你将一系列的链接组合在一起&#xff0c;就形成了文档的一个 division。 <p>标签&#xff1a;定义段落...

系统架构设计师考试大纲2023

一、 考试方式&#xff08;机考&#xff09; 考试采取科目连考、 分批次考试的方式&#xff0c; 连考的第一个科目作答结束交卷完成后自动进 入第二个科目&#xff0c; 第一个科目节余的时长可为第二个科目使用。 高级资格&#xff1a; 综合知识科目考试时长 150 分钟&#xff…...

sqli.labs靶场(第18~22关)

18、第十八关 经过测试发现User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0加引号报错 这里我们闭合一下试试 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0,127.0.0.1,adm…...

【tensorflow 版本 keras版本】

#. 安装tensorflow and keras&#xff0c; 总是遇到版本无法匹配的问题。 安装之前先查表 https://master--floydhub-docs.netlify.app/guides/environments/ 1.先确定你的python version 2.再根据下面表&#xff0c;确定安装的tesorflow, keras...

嵌入式学习第十六天

制作俄罗斯方块小游戏&#xff08;一&#xff09; 分析&#xff1a; printf函数高级用法 \033[&#xff1a;表示转义序列的开始 m&#xff1a;表示转义序列的结束 0&#xff1a;重置所有属性 1&#xff1a;设置粗体或高亮 30-37&#xff1a;设置字体色 30: 黑 31: 红 32:…...

Java过滤器拦截器的区别和实现

一、什么是过滤器与拦截器&#xff1f; 1.2 拦截器&#xff08;Interceptor&#xff09; java过滤器指的是在java中起到过滤的作用的一个方法。可以在一个请求到达servlet之前&#xff0c;将其截取进行逻辑判断&#xff0c;然后决定是否放行到请求的servlet&#xff1b;也可以在…...

Android 12 系统开机动画

修改Android开机动画有两种方式 方式一、通过adb 命令来修改&#xff1a; 进入/system/media目录&#xff0c;将里面的 bootanimation.zip 文件pull出来&#xff0c;然后解压&#xff0c;替换part0和part1中的图片&#xff0c;并且根据图片大小修改文件 desc.txt 中的内容&…...

C++:异常体系

异常体系 异常1.C语言传统的处理错误的方式2.C异常概念3.异常的使用3.1异常的抛出和捕获3.2 异常的重新抛出3.3异常安全3.4 异常规范 4.C标准库的异常体系5.异常的优缺点 异常 1.C语言传统的处理错误的方式 终止程序&#xff0c;如assert&#xff0c;缺陷&#xff1a;用户难以…...

民事二审案件庭审应如何准备?

一、你要明确审理范围&#xff0c;固定上诉请求 首先&#xff0c;第二审人民法院围绕当事人的上诉请求进行审理。 其次&#xff0c;在第二审程序中&#xff0c;原审原告增加独立的诉讼请求或者原审被告提出反诉的&#xff0c;第二审人民法院可以根据当事人自愿的原则就新增加的…...

WebRTC系列-H264视频帧组包(视频花屏问题)

文章目录 工具函数是否满足组帧条件函数PotentialNewFrame更新丢失包记录 UpdateMissingPackets重要属性1. InsertPacket2. FindFramesWebRTC在弱网环境下传输较大的视频数据,比如:屏幕共享数据;会偶发的出现黑屏的问题;也就是说当视频的码率比较大且视频的分辨率比较高的时…...

Common Mistakes in German

Comman Mistakes in German 1, Haus oder Hause2, ja nein oder doch(1) Positive Fragen(2) Negative Fragen 1, Haus oder Hause 2, ja nein oder doch (1) Positive Fragen (2) Negative Fragen kein / nicht P3...

Java基础数据结构之反射

一.定义 Java的反射机制是在运行状态中的&#xff0c;对于任意一个类都能知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意方法及属性。既然能拿到&#xff0c;我们就可以修改部分类型信息。这种动态获取信息以及动态调用对象方法的功能…...

【实战系列----消息队列 数据缓存】rabbitmq 消息队列 搭建和应用

线上运行图&#xff0c;更新不算最新版&#xff0c;但可以使用修改线程等补丁功能&#xff0c;建议使用新版本。 远程服务器配置图: 这个可以更具体情况&#xff0c;因为是缓存队列理所当然 内存越大越好&#xff0c;至于核心4核以上足够使用。4核心一样跑 这里主要是需要配置服…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...