万字完整版【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.指针相关习题未完待续
  • 此文章发布之后,一个月内必更新~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【语法基础练习】1.变量、输入输出、表达式与顺序语句

&#x1f338;博主主页&#xff1a;釉色清风&#x1f338;文章专栏&#xff1a;算法练习&#x1f338;今日语录&#xff1a;You don’t know until you try. 文章简介&#xff1a;下面的题目是AcWing网站语法基础练习篇的第一小节&#xff0c;内容基础&#xff0c;难度&#xf…

如何进入Windows 11的安全模式?这里提供详细步骤

序言 如果你在启动Windows 11 PC时遇到问题,则重新启动到安全模式可能会有所帮助,该模式会暂时禁用驱动程序和功能,以使你的PC更稳定。下面是如何做到这一点。 在Windows 7和更早版本中,通常可以在打开电脑后按功能键(如F8)启动安全模式。Microsoft从Windows 8中删除了…

MybatisPlus知识点总结(基于黑马2023MybatisPlus课程)

MybatisPlus知识点总结 配套资料 黑马微服务框架笔记&#xff0c;内含mpMybatisPlus.pptxMyBatis-Plus &#xff08;mp中文官网&#xff09; 快速入门 入门案例 使用MybatisPlus的基本步骤&#xff1a;1.引入MybatisPlus依赖&#xff0c;代替Mybatis依赖2.定义Mapper接口并继…

C++_可变参数模板

目录 1、可变参数模板的用法 2、参数包展开 2.1 递归方式展开 2.2 逗号表达式形式展开 3、参数包的大小 结语 前言&#xff1a; C11引入了可变参数模板&#xff0c;他的作用是可以让创建的函数模板或者类模板的模板参数可以接收任意数量参数&#xff0c;在C11前&#x…

16. C++标准库

C标准库兼容C语言标准函数库&#xff0c;可以在C标准库中直接使用C语言标准函数库文件&#xff0c;同时C标准库增加了自己的源代码文件&#xff0c;新增文件使用C编写&#xff0c;多数代码放在std命名空间中&#xff0c;所以连接C标准库文件后还需要 using namespace std;。 【…

【OpenGL手册11】材质的模型

目录 一、说明二、材质表面和光照三、设置材质四、光的属性五、不同的光源颜色练习 一、说明 在现实世界里&#xff0c;每个物体会对光产生不同的反应。比如&#xff0c;钢制物体看起来通常会比陶土花瓶更闪闪发光&#xff0c;一个木头箱子也不会与一个钢制箱子反射同样程度的…

Seata 2.x 系列【6】微服务项目搭建

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Seata 版本 2.0.0 本系列Spring Boot 版本 3.2.0 本系列Spring Cloud 版本 2023.0.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 概述2. 数据库…

2024年3月ZZUACM 招新赛题解

2024年3月ZZUACM 招新赛 题号题目A区间次大值B上课签到C魔法森林&#xff08;一&#xff09;D魔法森林&#xff08;二&#xff09;ELOPF跳格子G猜数字H抽卡记录I安达的二维矩阵J安达的数字手术K跳楼梯L前缀和 A 区间次大值—循环/签到题 题目描述 给定一个 n n n的全排列 a i…

DeepLearning in Pytorch|共享单车预测NN详解(思路+代码剖析)

目录 概要 一、代码概览 二、详解 基本逻辑 1.数据准备 2.设计神经网络 初版 改进版 测试 总结 概要 原文链接&#xff1a;DeepLearning in Pytorch|我的第一个NN-共享单车预测 我的第一个深度学习神经网络模型---利用Pytorch设计人工神经网络对某地区租赁单车的使用…

自然语言处理: 第十三章P-tuing系列之P-tuning V1

项目地址: P-Tuning 论文地址: [2103.10385] GPT Understands, Too (arxiv.org) 理论基础 正如果上一节介绍LoRA(自然语言处理: 第十二章LoRA解读_lora自然英语处理-CSDN博客)一样,本次介绍的在21年由清华团推提出来的 P-Tuning V1系列也属于PEFT(参数高效微调系列)里的一种&…

【前端】平面转换与渐变

目录 1.字体图标 2.平面转换 2.1位移 2.2旋转 2.3多重转换 2.4缩放 3.渐变 1.字体图标 引入iconfont平台字体图标样式表 <link rel"stylesheet" href"./iconfont/iconfont.css"> <i class"iconfont xx"></i> 第一个类…

函数柯里化:JavaScript中的高级技巧

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

学习嵌入式C语言要掌握到什么程度?

学习嵌入式C语言要掌握到什么程度&#xff1f; 在开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些 电气工程师学习方法和资料&#xff0c;让我不断提升自己&#…

anaconda问题合集

目录 一. 万分注意 二. ImportError: DLL load failed while importing _ctypes: 找不到指定的模块。 1. 发生情况 2. 导致结果和解决方法 三. WARNING: A newer version of conda exists. 1. 在conda install 某库的时候 2. 解决方法 一. 万分注意 不要轻易使用 conda …

YOLO建筑物损伤评估数据集

YOLO建筑物损伤评估数据集&#xff0c;重度损伤&#xff0c;轻微损伤&#xff0c;中度损伤&#xff0c;未损伤4类&#xff0c;近五千张图像&#xff0c;yolo标注完整&#xff0c;应用数据增强。 适用于CV项目&#xff0c;毕设&#xff0c;科研&#xff0c;实验等 需要此数据集…

举个栗子!Alteryx 技巧(10):解决连数据库时出现中文乱码的问题

在日常工作中&#xff0c;我们经常遇到需要连接 Oracle 数据库的场景。当采用 ODBC 的方式进行连接时&#xff0c;可能会出现中文乱码。那么&#xff0c;应该如何解决中文乱码这个问题呢&#xff1f; 本期《举个栗子&#xff01;Alteryx 技巧》&#xff0c;我们就来分享方法吧…

近年来文本检测相关工作梳理

引言 场景文本检测任务&#xff0c;一直以来是OCR整个任务中最为重要的一环。虽然有一些相关工作是端对端OCR工作的&#xff0c;但是从工业界来看&#xff0c;相关落地应用较为困难。因此&#xff0c;两阶段的OCR方案一直是优先考虑的。 在两阶段中&#xff08;文本检测文本识…

猫头虎分享已解决Bug || 云服务中断:CloudOutage, CloudProviderError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

GO的运算符

点击名片关注 阿尘blog&#xff0c;一起学习&#xff0c;一起成长 Go语言中的运算符用于执行各种操作&#xff0c;如算术运算、比较、逻辑运算等。下面是Go语言中各类运算符的详细讲解&#xff0c;包括代码示例、代码说明以及使用注意事项。 1 算术运算符 加法运算符 a : 5 b …

Java实现从本地读取CSV文件数据

一、前言 最近项目中需要实现这样一个功能&#xff0c;就是从本地读取CSV文件&#xff0c;并以指定行作为标题行&#xff0c;指定行开始作为数据读取行&#xff0c;读取数据并返回给前端&#xff0c;下面具体说下是如何通过java实现。 二、如何实现&#xff1f; 1.引入相关mav…