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

目录

一.内存和地址 

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.mzph.cn/news/662195.shtml

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

相关文章

2024年电子数据取证“獬豸杯”比赛解析WP

2024年电子数据取证“獬豸杯”比赛解析WP 项目介绍:参赛对象:任务目标:第一部分:手机取证第二部分:计算机取证第二部分:APK分析 项目介绍: 简介: 竞赛为个人赛,工具自备&#xff0c…

如何实现任意设备远程SSH访问Deepin操作系统【内网穿透】

文章目录 推荐前言1. 开启SSH服务2. Deppin安装Cpolar3. 配置ssh公网地址4. 公网远程SSH连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击跳…

2023年出版的新书中提到的《人月神话》(202402更新)(1)

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 《人月神话》于1975年出版,1995年出二十周年版。自出版以来,该书被大量的书籍和文章引用,直到现在热潮不退。 2023年,清华大学出版社推…

phpMyAdmin 未授权Getshell

前言 做渗透测试的时候偶然发现,phpmyadmin少见的打法,以下就用靶场进行演示了。 0x01漏洞发现 环境搭建使用metasploitable2,可在网上搜索下载,搭建很简单这里不多说了。 发现phpmyadmin,如果这个时候无法登陆,且也…

开源软件:颠覆创新的力量

开源软件是一种基于共享和协作的软件开发模式,通过允许源代码的自由访问和修改,开源软件为全球范围内的开发者和用户提供了极大的灵活性和自由度。在过去的几十年里,开源软件在全球范围内产生了巨大的影响力,对技术创新、商业模式…

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

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

【读点论文】A Survey of Deep Learning Approaches for OCR and Document Understanding

A Survey of Deep Learning Approaches for OCR and Document Understanding Abstract 文档是许多领域(如法律、金融和技术等)中许多业务的核心部分。自动理解发票、合同和简历等文件是有利可图的,开辟了许多新的商业途径。通过深度学习的发展,自然语言…

IntelliJ IDEA的常用插件收集

Alibaba Java Coding Guidelines : (代码质量检查)ChatGPT GPT-4 - Bito AI (使用GPT4.0的AI工具)Tabnine: AI Code Completion (使用AI自动完成代码编写)Translation (中英文翻译)jclasslib Bytecode viewer (字节码源文件查看,主要用来分析底层JVM的调用流程)Free…

docker中三种常用的持久化数据的方式

文章目录 介绍1.docker run -v2.volumes3.bind mounts 介绍 “前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。” 在Docker中,有以下三种常用的持久化数据的方式,可…

redis 极简分布式锁实现

写在前面 工作中遇到,整理 reids 做简单分布式锁的思考博文适合刚接触 redis 的小伙伴理解不足小伙伴帮忙指正 对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是…

【MySQL】MySQL库

使用C/C语言链接MySQL 一、mysql connect二、mysql 接口介绍1. 初始化 mysql_init()2. 链接数据库 mysql_real_connect()3. 执行 mysql 命令 mysql_query()4. 获取执行结果 mysql_store_result()5. 释放空间5. 关闭 mysql 链接 mysql_close() 一、mysql connect 要使用C语言连…

Oracle篇—普通表迁移到分区表(第五篇,总共五篇)

☘️博主介绍☘️: ✨又是一天没白过,我是奈斯,DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux,也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章,并且也会默默的点赞收藏加关注❣…

【深度学习】ND4J-科学计算库

目录 简介 基础用法 基础信息 数组创建 打印数组 变更维度&堆叠 加减乘除 累加/最大/最小 转换操作 矩陈乘法 索引/迭代 深拷贝/引用传递/视图 引用传递 视图 深拷贝 其它 简介 ND4J主要是JVM的科学计算库,内置了很多计算方法,目的…

利用tshark从pcap中解析http流量

使用tshark解析 安装tshark apt install tshark # 测试 tshark -r gitlab.pcap -T fields -Y http -e tcp.stream -e http.request.method -e http.request.uri -e http.request.version -e http.request.line -e http.response.version -e …

【AIGC扫盲】人工智能大模型快速入门

人工智能大模型的技术框架主要有以下几种: TensorFlow:这是一个由Google Brain团队开发的开源库,用于进行高性能数值计算,特别是用于训练和运行深度学习模型。TensorFlow提供了一种称为计算图的编程模型,它允许用户定义…

MS Access 函数参考手册

目录 MS Access 字符串函数 MS Access 数值函数 MS Access 日期函数 MS Access 其他函数 MS Access 字符串函数 函数描述Asc返回特定字符的 ASCII 值Chr返回指定ASCII码的字符Concat with &将两个或多个字符串加在一起CurDir返回指定驱动器的完整路径Format用指定的格…

如何在CentOS安装DataEase数据分析服务并实现远程访问管理界面

如何在CentOS安装DataEase数据分析服务并实现远程访问管理界面 前言1. 安装DataEase2. 本地访问测试3. 安装 cpolar内网穿透软件4. 配置DataEase公网访问地址5. 公网远程访问Data Ease6. 固定Data Ease公网地址 🌈你好呀!我是 是Yu欸 🌌 202…

IDEA中的Run Dashboard

Run Dashboard是IntelliJ IDEA中的工具【也就是View中的Services】,提供一个可视化界面,用于管理控制应用程序的运行和调试过程。 在Run DashBoard中,可以看到所有的运行配置,以及每个配置的运行状态(正在运行&#xf…

【vue2源码】阶段一:Vue 初始化

文章目录 一、项目目录1、主目录2、打包入口 二、构造函数Vue的初始化1、创建 Vue 构造函数2、初始化内容分析2.1 initMixin2.2 stateMixin2.3 eventsMixin2.4 lifecycleMixin2.5 renderMixin 一、项目目录 源码版本:2.7.16 1、主目录 src |-- compiler # 包…

Camille-学习笔记-web基础知识

web基础1.系统架构 B/S :Browser/Server 网站 界面层(UI) 业务逻辑层(业务) 数据访问层(数据库) 静态网页:和服务器没有数据交互 动态网页:网页数据可以和服务器进行数据交互 URL…