万字完整版【C语言】指针详解~

一、前言

  • 初始指针(0):着重于讲解指针的概念、基本用法、注意事项、以及最后如何规范使用指针
  • 深入指针(1):讲解指针变量常见的类型,如何去理解这些类型、最后就是如何正确的使用
  • 深入指针(2):讲解指针较为高阶的用法,理解难度 中度
  • 深入指针(3):在(2)的基础上,再讲解其余指针类型的高级用法,理解难度 偏难
  • 深入指针(4):指针相关的题目、及答案。理解难度 中度

二、初识指针(0)

1.内存与地址
  • CPU(中央处理器),需要向内存读取数据,处理完数据后,还需要将数据放回到内存中,那CPU是如何从内存找到他自己想要的数据呢?        
  • 现实中,我们去向宾馆申请一间房间,前台的服务人员,会给我们找出一间闲置的空间供给我们使用,并返回给我们一个房卡。那她是如何快速的找到一间闲置的空间呢?那是因为每一间闲置的空间都有编号。当我们不用的时候,她们就会收回这间屋子的使用权。
  • 我们将CPU抽象为前台服务人员,把内存抽象为宾馆的房子,把CPU读取数据抽象为去宾馆申请房子,把CPU返回数据抽象为收回这间屋子的使用权......
  • 我们就可以推断出,内存也被划分了很多个内存单元,每一个内存单元有自己的专属编码
  • 结论:内存被划分为很多个大小为一个字节的内存单元,每一个内存单元都有相对应的内存编码,内存编码又叫做地址。CPU处理数据的时候,则是根据地址去找到所需的数据。
2.指针变量与地址
  • 有时候,我们自己要处理数据的时候,也必不可少要用到地址,所以C语言允许我们获得变量的地址,并使用地址,或者通过地址找到该数据,进行使用。
  • C语言规定了一个指针变量,专门用来存储数据地址,并且有两个操作符(取地址操作符&,解引用操作符*)。
  • 下面是其简单的使用:
  • 结论:内存单元编码 == 地址 == 指针
3.指针类型的刨析
i.指针的理解 
  • (type) * p;

  • p     ->p是一个变量
  • *      ->变量p前面的(*),说明它是一个指针类型的变量
  • type->说明了指针变量p,所指向对象的类型
ii.指针在空间的大小
  • 了解过音乐的都知道,音乐有这几个音阶(do、re、mi、fa、sol、la、si),在钢琴制作的时候,也没有把哪个键标记下音阶,那演奏家是如何找到音阶的吗?那是因为音阶的分布是被死规定过的,且是公认的,无论哪几种钢琴,它的音阶所在按键位置是不变的。
  • 事实上,内存单元的编号也被按照某种标准的规定所设定,计算机中存在地址线,正电流表示二进制1,负电流表示二进制0;在32位平台上,由32根地址线的表示一个内存单元的地址,也就是一个整型,在64位平台上,由64根地址线的表示一个内存单元的地址,也就是两个整型。
  • (int)类型在内存中占据4个字节,但我们取它地址的时候,常用第一个内存单元的地址,作为该int类型的地址。
  • 小结:在32位平台上,地址由32比特位构成,也就是1个(int)类型的大小,4个字节;在64位平台上,地址由64位比特位构成,也就是2个(int)类型的大小,8个字节。无论什么类型的指针,只要是用来存放地址的,其指针类型的字节大小只与平台有关->(4/8)。

iii.指针类型有什么用? 

  • 这里我就不卖关系了,直接告诉你们结论:
  • 指针类型决定着,指针变量解引用后所能操作的空间
  • 指针类型决定着,指针变量+/-n个整数后,指针的地址变化的字节(为指针所指向对象的类型在内存空间占用字节大小的n倍)。
4.const修饰指针
  • 关键字const的作用:
  • 使变量,具有常量属性->其变量所存储的数据,不可被修改(但它仍算是变量,并非我们常说的常量) 
  • const在(*)的左边,const修饰的是指针变量所指向的对象不能够被修改。
  • const在(*)的右边,const修饰的是指针变量所存储的地址不能够被修改。
5.指针的运算
i.指针+/-整数
  • 指针 +/- 整数(n) = 指针当前的值 +/- n*(指针所指向对象的类型在内存中所占用的字节大小)
ii.指针-指针
  • 在我日常生活都会用到日期,月份 +/- 天数 = 月份;月份 - 月份 = 天数。
  • 如果我们把月份抽象为指针,把整数(n)抽象为天数。我们就能推测出指针 - 指针 = 此整数(n);n的意义是:两个指针之间元素的个数。
  • 注:两个指针必须指向同一块空间,比如一个一维数组,一个指针指向数组的首元素,另一个指针指向数组的末元素,结果就是数组的元素的个数。就好比农历的月份 - 阳历的月份没有意义。
iii.指针的关系运算
  • 指针的关系运算很好理解,就是指针之前可以进行(<,>,<=,<=)逻辑运算。毕竟地址是有高地址与低地址的,且地址是由32比特位或64比特位表示的。自然就能进行逻辑运算。
6.void* 类型的指针
  • (void*)类型的指针 :可接收任意类型的指针,但不可解引用,也不可进行指针的关系的运算。
  • void* 指针常用来做函数的参数或返回值,
  • 具体案例请看下面的冒泡排序的思想封装一个可以比较任意类型的一组数据的函数。
7.野指针

i.什么是野指针

  • 野指针就是一个指针变量指向的空间,或是随机值或已经被释放,总之就是未经操作系统允许的空间,好比你去宾馆开房,房子都回收了,你再去使用是不合法。

ii.野指针产生的情景

  • 指针未被初始化:
  • 了解过函数栈帧的都知道,局部变量未初始化,其指向的数据是0xcccccc,是随机值。因为VS检查太严格,无法有具体的代码案例。
  • 指针访问越界:
  • 指针指向了已被释放的空间:
8.如何避免野指针
  • 前面我们已经知道了指针越界的三种情况,我们在使用指针的时候如何避免野指针?
  • 指针变量要记得初始化。
  • 尽量避免使用指针越界的情况。
  • 将暂时用不到的指针变量赋值成空指针:NULL。NULL的值为0,内存中,并不是所有的内存是允许我们访问的,会有一部分内存空间留给操作系统。所以访问NULL会之间报错,你用都用不了。这样也能让我们就算有错,也能知道。
  • 检测是否为NULL:若能做到上面的操作,则我们使用的指针要么是NULL,要么就是合法的指针。所以在使用指针的使用用if语句作判断,若指针不为NULL,才使用。

三、深入指针(1)

1.二级指针变量
  • 二级指针:
  • 接收一级指针的指针,(type*) *pp;
  • pp是一个变量
  • *与pp结合,表示pp是一个指针变量
  • (type*) 的类型说明,pp指向对象的类型
  • 常用场景在函数传参的时候,参数有一级指针,且一级指针需要被更改的时候,我们就需要传递一个二级指针。 
2.字符指针变量
  • 字符指针
  • char* p;
  • p是一个变量
  • *与p结合,表示p是一个指针变量
  • char说话,p指向的对象是char类型
3.指针数组
  • 指针数组:
  • 是一个数组,数组的元素是指针;(type*)arr[整型常量N]
  • arr先与 [] 结合,表明它是变量arr是数组名,其中整型常量N表示该数组含有N个元素
  • (type*)表示该数组每一个的元素的指针类型;
4.数组指针变量
  • 数组指针:
  • 是一个指针,指向的对象是一个数组;(type)(*p)[整型常量N]。
  • p先于(*)结合,表示它是一个指针,因为[]的结合性强于*,所有得用上()。
  • 数组的类型是 (type) [整型常量N];
5.函数指针变量
  • 函数指针:
  • 是一个指针,指向的对象是一个函数;(type) (*p) (type,type)
  • p先于(*)结合,表示它是一个指针,
  • 后与 () 结合,表面p指向的对象是一个函数。
  • 括号里的两个type,表明函数的参数的类型,前面的type,表示函数的返回类型。
  • 注:函数名与&函数名,所代表的含义一致
6.函数指针数组
  • 函数指针数组:
  • 是一个数组,数组的元素的指针,但这里有的特殊,数组的元素是函数指针;
  • 带你们分析理解了那么多个类型,这个自主分析
  • 代码案例,转移表;下面讲的有。
7.typedef关键字
  • 关键字typedef:   
  • 将一个数据类型重定义,使复杂的类型,可以简单化;
  • typedef type NEW_type
  • 将类型为type的,重定义为NEW_type,
  • 我们可以用type类型创建一个变量,也可以用NEW_type;
  • 注:NEW_type与type是完全等价的,与#define宏定义的符合替换不同

四、深入指针(2)

1.一维数组:数组名的理解
  • 数组名:
  • 一般情况数组首元素的地址
  • 除了关键字sizeof后面的数组名,与&数组名
2.二维数组:数组名的理解
  • 数组名:
  • 一般情况数组首元素的地址
  • 除了关键字sizeof后面的数组名,与&数组名
  • 二维数组的元素是?
  • 二维数组是将每一组一维数组当作它的元素。
  • 所以数组的首元素的地址是第一行数组的地址,
3.一维数组传参
4.二维数组传参
5.指针数组模拟二维数组
i.模拟思路
ii.代码实现
6.二级指针模拟二维数组
i.模拟思路
  • 注:int*类型的元素,是连续的,常用的情景是动态开辟空间模拟二维数组。
ii.代码实现

五、深入指针(3)

1.转移表
  • 转移表就是函数指针数组;将所需要的函数的指针,存储在一个数组中,通过数组得下标去调用函数,这样就能避免代码的冗余。使得代码又清晰,又简化。
  • //转移表代码实践->计算器功能得实现
    #include<stdio.h>
    void menu()
    {printf("************************\n");printf("**** 0.exit   2.sub ****\n");printf("**** 1.Add    3.Mul ****\n");printf("**** 4.div    5.Mor ****\n");printf("************************\n");
    }
    int Add(int x, int y)
    {return x + y;
    }
    int Sub(int x, int y)
    {return x - y;
    }
    int Mul(int x, int y)
    {return x * y;
    }
    int Div(int x, int y)
    {return x / y;
    }
    int Mor(int x, int y)
    {return x % y;
    }
    typedef int(*Fun[])(int, int);
    int main()
    {Fun pf = { 0, Add,Sub ,Mul,Div,Mor };int input = 0;do {menu();printf("请下指令:>");scanf("%d", &input);if (input <= 5 && input >= 0){int m, n;printf("请输入两个操作数:>");scanf("%d%d", &m, &n);int ret = pf[input](m, n);printf("%d\n", ret);}else{(input == 0 ? printf("关闭计算机\n") : 1);continue;}} while (input);return 0;
    }
3.回调函数
  • 回调函数就是⼀个通过函数指针调⽤的函数。
    如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应
4.qsort函数
  • 排序神器——qsort函数之详解-CSDN博客
  • 此链接是专门讲解得qsort函数的功能以及用法
  • 快速排序【全方面讲解快排,此文足以彻底扫盲】-CSDN博客
  • 此链接则是针对qsort函数内部的代码实现逻辑进行讲解——快速排序
5.回调函数小试牛刀---冒泡排序模拟qsort的功能
  • 冒泡排序进阶之模拟qsort排序函数(回调函数用法讲解)-CSDN博客

六、深入指针(4)

1.关键字sizeof
  • sizeof:
  • 关键字
  • 计算变量的类型在内存中所占用字节的大小,也就是说它括号里面的式子不参与计算,仅仅判断一下最终结果的类型,然后计算该类型在内存中所占用字节的大小,后返回回来。
2.库函数strlen
  • strlen:
  • 库函数,使用的时候,需要包含<string.h>头文件
  • 计算字符串'\0'之前的元素个数,也就说,strlen的停止条件是遇到'\0';所以strlen只能计算字符串的长度。
3.数组名含义的回顾【一维】
  • 数组名:
  • 一般情况数组首元素的地址
  • 除了关键字sizeof后面的数组名,与&数组名
4.数组名含义的回顾【二维】
  • 数组名:
  • 一般情况数组首元素的地址
  • 除了关键字sizeof后面的数组名,与&数组名
  • 二维数组的元素是?
  • 二维数组是将每一组一维数组当作它的元素。
  • 所以数组的首元素的地址是第一行数组的地址,
5.指针相关习题未完待续
  • 此文章发布之后,一个月内必更新~

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

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

相关文章

【sgExcelGrid】自定义组件:简单模拟Excel表格拖拽、选中单元格、横行、纵列、拖拽圈选等操作

特性&#xff1a; 可以自定义拖拽过表格可以点击某个表格&#xff0c;拖拽右下角小正方形进行任意方向选取单元格支持选中某一行、列支持监听selectedGrids、selectedDatas事件获取选中项的DOM对象和数据数组支持props自定义显示label字段别名 sgExcelGrid源码 <template&g…

Swift 入门学习:集合(Collection)类型趣谈-下

概览 集合的概念在任何编程语言中都占有重要的位置&#xff0c;正所谓&#xff1a;“古来聚散地&#xff0c;宿昔长荆棘&#xff1b;游人聚散中&#xff0c;一片湖光里”。把那一片片、一瓣瓣、一粒粒“可耐”的小精灵全部收拢、吸纳的井然有序、条条有理&#xff0c;怎能不让…

React Three Fiber快速入门

https://threejs-journey.com/lessons/what-are-react-and-react-three-fiber#学习笔记 1.基础知识 resize 填充模版 构建第一个场景 we didn’t have to create a scenewe didn’t have to create the webglrenderthe scene is being rendered on each framethe default…

ubuntu23.10安装搜狗拼音

1.添加fcitx仓库 sudo add-apt-repository ppa:fcitx-team/nightly 更新: sudo apt-get update 安装fcitx sudo apt-get install fcitx fcitx安装成功 切换输入系统为fcitx

【C语言】tcp_transmit_skb

一、__tcp_transmit_skb讲解 这个函数 __tcp_transmit_skb() 是 Linux 内核中 TCP/IP 协议栈的一部分&#xff0c;负责处理传输控制协议&#xff08;TCP&#xff09;数据包的发送。具体来说&#xff0c;这个函数将 TCP 头部添加到一个没有任何头部信息的 socket buffer (sk_bu…

Diddler抓包工具——学习笔记

F12抓包 302【重定向】&#xff1a;当你发送了一个请求之后&#xff0c;那么这个请求重定向到了另外的资源 跳转和重定向的区别&#xff1a; 跳转是会把数据传到新的地址 重定向不会把新的数据传到新的地址 使用F12抓包时一定要打开Preserve Log开关&#xff0c;作用是保留…

万用表数据导出变化曲线图——pycharm实现视频数据导出变化曲线图

万用表数据导出变化曲线图——pycharm实现视频数据导出变化曲线图 一、效果展示二、环境配置三、代码构思四、代码展示五、代码、python环境包链接 一、效果展示 图1.1 效果展示 &#xff08;左图&#xff1a;万用表视频截图&#xff1b;右图&#xff1a;表中数据变化曲线图&am…

Springboot整合Mybaits启动过程

Springboot整合Mybaits启动过程 1.前言2.MybatisAutoConfiguration3.SqlSessionFactoryBean3.1 XMLConfigBuilder.parse()3.1.1 XMLMapperBuilder.parse()3.1.1.1 XMLStatementBuilder.parse() 4.SqlSession4.1 Executor 1.前言 直接加载mybatis配置文件&#xff0c;然后创建S…

Matlab|【EI复现】电动汽车集群并网的分布式鲁棒优化调度模型

目录 1 内容简介 2 关键知识点 2.1 三类电动汽车模型 3 程序结果 4 下载链接 1 内容简介 电动汽车的数据模型种类繁多&#xff0c;但是用到比较高阶数学方法的并不多&#xff0c;本次分享的程序是下图所示的文章。 采用分布鲁棒优化模型&#xff0c;用到鲁棒对等转换&…

MyBatis3源码深度解析(七)JDBC单连接事务

文章目录 前言2.7 JDBC单连接事务2.7.1 事务的开启与提交2.7.2 事务隔离级别2.7.2.1 并发访问问题&#xff08;1&#xff09;脏读&#xff08;2&#xff09;不可重复读&#xff08;3&#xff09;幻读 2.7.2.2 事务隔离级别&#xff08;1&#xff09;TRANSACTION_NONE&#xff1…

ChatGPT 串接到 Discord - 团队协作好助理

ChatGPT 串接到 Discord - 团队协作好助理 ChatGPT 是由 OpenAI 开发的一个强大的语言模型&#xff0c;本篇文章教你如何串接 Discord Bot &#xff0c;协助团队在工作上更加高效并促进沟通与协作。使 ChatGPT 发挥出最大的功效&#xff0c;进一步提升工作效率和团队协作能力。…

Redis 内存的优化

目录 前言 Redis 的内存碎片问题 判断Redis 内存碎片 如何清理内存碎片&#xff1f; 前言 我想讲一下怎么提高Redis 内存的利用率&#xff0c;redis 的数据是保存在内存中。对内存的利用率低&#xff0c;意味着存的数据很少&#xff0c;并不意味着就没有内存了&#xff0c…

【解读】OWASP大语言模型应用程序十大风险

OWASP大型语言模型应用程序前十名项目旨在教育开发人员、设计师、架构师、经理和组织在部署和管理大型语言模型&#xff08;LLM&#xff09;时的潜在安全风险。该项目提供了LLM应用程序中常见的十大最关键漏洞的列表&#xff0c;强调了它们的潜在影响、易利用性和在现实应用程序…

利用华为CodeArts持续交付项目演示流程

软件开发生产线&#xff08;CodeArts&#xff09;是面向开发者提供的一站式云端平台&#xff0c;即开即用&#xff0c;随时随地在云端交付软件全生命周期&#xff0c;覆盖需求下发、代码提交、代码检查、代码编译、验证、部署、发布&#xff0c;打通软件交付的完整路径&#xf…

力扣---腐烂的橘子

题目&#xff1a; bfs思路&#xff1a; 感觉bfs还是很容易想到的&#xff0c;首先定义一个双端队列&#xff08;队列也是可以的~&#xff09;&#xff0c;如果值为2&#xff0c;则入队列&#xff0c;我这里将队列中的元素定义为pair<int,int>。第一个int记录在数组中的位…

day15_集合_ArrayList

今日内容 零、 复习昨日 一、集合框架体系 二、Collection 三、泛型 四、迭代 五、List(ArrayList、LinkedList) 零、 复习昨日 日期解析的方法签名(字符串–>日期) Date parse(String s) 日期格式化的方法签名(日期–>字符串) String format(Date date) 运行时异常有哪些…

19、电源管理入门之微内核中的电源管理

目录 1. QNX电源管理框架 2. QNX客户端API库 3. QNX代码分析 4. Fuchsia中的电源管理 5. Minix中的电源管理 6. Harmony OS中的电源管理 之前介绍的电源管理机制基本都是在Linux中实现的,可以看到很复杂,各种框架,明明一个操作非要转来转去,而且在内核里面实现,跟内…

【HarmonyOS】ArkTS-联合类型

目录 联合类型实例 联合类型 联合类型是一种灵活的数据类型&#xff0c;它修饰的变量可以存储不同类型的数据。 语法&#xff1a;let 变量: 类型1 | 类型2 | 类型3 值 基于联合类型&#xff0c;变量可存不同类型数据 实例 // 需求&#xff1a;定义一个变量&#xff0c;存放…

Spring web开发(入门)

1、我们在执行程序时&#xff0c;运行的需要是这个界面 2、简单的web接口&#xff08;127.0.0.1表示本机IP&#xff09; package com.example.demo;import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestCont…

【OD】算法二

开源项目热度榜单 某个开源社区希望将最近热度比较高的开源项目出一个榜单&#xff0c;推荐给社区里面的开发者。对于每个开源项目&#xff0c;开发者可以进行关注(watch)、收藏(star)、fork、提issue、提交合并请求(MR)等。 数据库里面统计了每个开源项目关注、收藏、fork、…