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

news2025/1/12 4:42:21

一、前言

  • 初始指针(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.coloradmin.cn/o/1511613.html

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

相关文章

语音情感基座模型emotion2vec

在语音技术领域&#xff0c;准确理解用户的语音指令和意图是构建高效人机交互系统的基础。一个高品质的语音交互系统不仅需要理解字面上的语言内容&#xff0c;更应捕捉到说话者语音中蕴含的情感信息。这正是语音情感识别&#xff08;SER&#xff09;技术要解决的问题&#xff…

【libwebrtc】基于m114的构建

libwebrtc A C++ wrapper for binary release, mainly used for flutter-webrtc desktop (windows, linux, embedded).是 基于m114版本的webrtc 最新(20240309 ) 的是m122了。官方给出的构建过程 .gclient 文件 solutions = [{"name" : src,"url

Caffeine缓存

本地缓存基于本地环境的内存&#xff0c;访问速度非常快&#xff0c;对于一些变更频率低、实时性要求低的数据&#xff0c;可以放在本地缓存中&#xff0c;提升访问速度 使用本地缓存能够减少和Redis类的远程缓存间的数据交互&#xff0c;减少网络 I/O 开销&#xff0c;降低这…

Python 导入Excel三维坐标数据 生成三维曲面地形图(体) 5-1、线条平滑曲面且可通过面观察柱体变化(一)

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata fro…

【软考中级】网络工程师:11.网络管理

11.1 网络管理五大功能域 故障管理、配置管理、计费管理、安全管理和性能管理。 故障管理&#xff1a;尽快发现故障&#xff0c;找出故障原因&#xff0c;以便找出补救措施。 配置管理&#xff1a;初始化、维护和关闭网络设备或子系统。 计费管理&#xff1a;计费监视主要是跟…

dubbo调用的自定义过滤器中设置MDC无法生效的问题

AI的解释 Dubbo自定义过滤器不生效可能有多种原因&#xff0c;以下是一些常见的原因及解决方法&#xff1a; 过滤器未正确配置&#xff1a; 检查过滤器是否已经在Dubbo的配置文件中正确声明&#xff0c;并且已经添加到过滤器链中。在XML配置中&#xff0c;应使用<dubbo:se…

Ubuntu 14.04:PaddleOCR基于PaddleServing的在线服务化部署(失败)

一、 二、安装 注&#xff1a; 安装 PaddleOCR 2.3 。 因为 PaddleOCR 2.4 的 推荐环境 PaddlePaddle > 2.1.2。 https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.4/doc/doc_ch/environment.md 安装前的环境准备 在使用Paddle Serving之前&#xff0c;需要完…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的常见车型识别系统(Python+PySide6界面+训练代码)

摘要&#xff1a;本文深入探讨了如何应用深度学习技术开发一个先进的常见车型识别系统。该系统核心采用最新的YOLOv8算法&#xff0c;并与早期的YOLOv7、YOLOv6、YOLOv5等版本进行性能比较&#xff0c;主要评估指标包括mAP和F1 Score等。详细解析了YOLOv8的工作机制&#xff0c…

【力扣白嫖日记】1321.餐馆营业额变化增长

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 1321.餐馆营业额变化增长 表&#xff1a;Customer 列名类型cutomer_idintnamevarcharvisited_ondateamounti…

阻塞队列学习

1、什么是阻塞队列&#xff1f; 顾名思义&#xff0c;就是支持阻塞的队列&#xff0c;相比于其他的队列&#xff0c;阻塞队列支持以下特性&#xff1a; 队列为空的时候&#xff0c;获取元素的线程会等待队列变为非空。队列为满的时候&#xff0c;存储元素的线程会等待队列可以…

初学者必会的Python3文件操作

文件操作的步骤&#xff1a; 打开文件 -> 操作文件 -> 关闭文件 切记&#xff1a;最后要关闭文件。 打开文件 文件句柄 open(文件路径, 模式) 指定文件编码 文件句柄 open(文件路径,模式,encodingutf-8) 为了防止忘记关闭文件&#xff0c;可以使用上下文管理器来…

计算机steam_api.dll丢失的解决方法,轻松搞定steam游戏问题

在计算机运行相关游戏软件时&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“由于找不到steamapi.dll&#xff0c;无法继续执行代码”&#xff0c;如果找不到steam_api.dll文件&#xff0c;将无法正常启动并运行基于Steam平台的游戏。。那么&#xff0c;如何…

镜面不锈钢氮气柜主要功能和应用领域介绍

镜面不锈钢氮气柜是一种专为特殊物品储存设计的高级储存设备&#xff0c;它结合了不锈钢材质的优良耐腐蚀性、易清洁性和氮气储存技术&#xff0c;确保内部储存的物品处于高度洁净、干燥且稳定的低氧环境中。以下是其主要功能介绍&#xff1a; 防潮保护&#xff1a;氮气柜通过填…

字节跳动也启动春季校园招聘了(含二面算法原题)

字节跳动 - 春招启动 随着各个大厂陆续打响春招的响头炮&#xff0c;字节跳动也官宣了春季校园招聘的正式开始。 还是那句话&#xff1a;连互联网大厂启动校招计划尚且争先恐后&#xff0c;你还有什么理由不马上行动&#xff1f;&#xff01; 先来扫一眼「春招流程」和「面向群…

双环PID控制详细讲解

参考博客&#xff1a; &#xff08;1&#xff09;PID双环控制&#xff08;速度环和位置环&#xff09; &#xff08;2&#xff09;PID控制&#xff08;四&#xff09;&#xff08;单环与双环PID&#xff09; &#xff08;3&#xff09;内外双环pid算法 0 单环PID 目标位置→系…

【spring】-多模块构建二-问题整理

1、bean注入问题 The injection point has the following annotations: - org.springframework.beans.factory.annotation.Autowired(requiredtrue) 解决1&#xff1a; 由于引入的bean类 不属于启动类的子模块下&#xff0c;需要在启动类手动声明扫描的类 也适用于公共子模…

探索HDFS读写流程、节点机制和数据完整性

目录 写在前面一、HDFS的读写流程1.1 HDFS写数据流程1.2 机架感知1.3 HDFS读数据流程1.4 小结 二、 NameNode和SecondaryNameNode2.1 NN和2NN工作机制2.2 Fsimage和Edits解析2.2.1 oiv查看Fsimage文件2.2.2 oev查看Edits文件 2.3 CheckPoint时间设置 三、DataNode3.1 DataNode工…

每日一题——LeetCode2129.将标题首字母大写

方法一 个人方法 将字符串转为数组&#xff0c;遍历数组&#xff0c;对数组的每一个元素&#xff0c;先全部转为小写&#xff0c;如果当前元素长度大于2&#xff0c;将第一个字符转为大写形式 var capitalizeTitle function(title) {titletitle.split( )for(let i0;i<tit…

[嵌入式系统-37]:龙芯1B 开发学习套件 -6-协处理器CP0之CPU异常处理与外部中断控制器的中断处理

目录 一、MPIS CPU Core与32个异常exception 1.1 龙芯1B的MIPS CPU IP Core 1.2 MIP32指令系统 1.3 MIPS CPU寄存器 1.4 龙芯异常exception与中断interrupt的区别 1.5 向量中断与非向量中断 1.6 MIPS CPU的异常向量与异常向量号&#xff1a;向量中断的支持 二、协议处理…

PAT-素数专题、质因子分解、大整数计算

素数专题 1007.素数对的猜想 让我们定义dn​为&#xff1a;​ &#xff0c;其中pi​是第i个素数。显然有d1​1&#xff0c;且对于n>1有dn​是偶数。“素数对猜想”认为“存在无穷多对相邻且差为2的素数”。 现给定任意正整数N(<105)&#xff0c;请计算不超过N的满足猜想…