指针详解——高级指针的解析及应用

news2024/11/28 11:44:46

目录

    🐑指针的初步了解

    🐂指针的深入认识

    🦛1.指针数组

    🐀指针数组的介绍

    🐀指针数组的用法介绍

    🐫2.数组指针

    🦌数组指针的介绍以及使用

    🦮3.函数指针

    🐈函数指针的介绍

    🐈函数指针的使用介绍

    🦘能力提升复杂指针的解析:

    🐫1.函数指针数组

    🦕2.数组指针数组

    🐧3.函数指针数组的指针

    🦏相信大家对于指针都是既熟悉又陌生,想深入了解有对指针感到畏惧。指针其实并不像大家想象的那样难于理解。在本次博客当中我们就来看一看指针的大家族都有哪些成员吧!

    🐑指针的初步了解

    🐘在了解高阶指针之前我们先来简单的介绍一下最简单的指针——一级指针。一级指针是我们最常见的指针,当我们想通过指针找到一个数据并将其改变的时候就可以通过指针进行改写。我们只需要创建一个相同类型的指针变量并把数据的地址交给指针变量保管即可。

#include<stdio.h>
int main()
{
	int num = 10;
	int* pa = &num;
	//通过指针变量的解引用找到数据的值
	printf("num=%d\n", *pa);
	//通过指针变量的解引用修改数据的值
	*pa = 20;
	printf("num=%d\n", num);
	return 0;
}

     🐘就像我们上面的示例,我们想要修改一个值,就可以利用指针很方便的进行操作,但是指针的使用并不是简简单单的修改一个变量的值而已,毕竟直接对变量进行修改也可以下到我们想要的效果。指针的价值在较长程序的书写当中往往会起到意想不到的作用。

    🐘接下来我们在来认识一个新的比较简单的指针概念——二级指针。对于二级指针相信有的小伙伴可能不像是一级指针那么熟悉,但是不要担心道理都是一样的。我们先来认识一下二级指针。

    🐘我们在使用指针的时候都是将数据的地址赋给指针变量,让指针变量进行代替保管。那么我们就很容易的可以想到:一个变量拥有地址,指针变量也是一个变量他是不是也有地址呢?指针变量的地址应该用什么来保存呢?那么这就涉及到我们的二级指针了。二级指针的作用就是保存我们一级指针变量的地址,并且进一步进行操作。使用的方法和上面的一级指针的使用方法是相同的。

#include<stdio.h>
int main()
{
	int num = 10;
	int* pa = &num;
	int** ppa = &pa;
	//通过二级指针变量的解引用找到数据的值
	printf("num=%d\n", **ppa);
	//通过二级指针变量的解引用修改数据的值
	**ppa = 20;
	printf("num=%d\n", num);
	return 0;
}

     🐘那么二级指针变量又该如何理解呢?我们在上图中样例中可以发现二级指针由两个 * 之后是一个变量,表示我们这个变量是一个二级指针变量。我们可以将我们的书写形式更改一下,更改为:int* (*ppa) =&pa; 这里我们可以将这样书写的代码理解成:因为括号具有最高的优先级所以我们要先解读括号里的内容(* ppa)代表主题是一个指针,那么是谁的指针呢?就需要向前找,发现类型为int * 那么我们就可以知道这是一个指针的指针,也就是二级指针。同样的道理我们还可以导出三级指针,四级指针,但是这些指针很难见到,所以我们先不做过多的介绍。

    🐘看到这里的时候相信你已经了解了一级指针和二级指针,但是这有什么用呢?别着急,我们接下来在高阶指针就带你体会到指针的妙用。

    🐂指针的深入认识

    🦛1.指针数组

    🐀指针数组的介绍

    🦔指针数组,是不是听着有点迷茫?这到底是一个什么东西呢?我们可以通过主次关系的思路进行理解什么是指针数组。相信大家在小学的时候都学过动宾关系,蓝蓝的天空,好看的花,漂亮的小姐姐......这些词语都是有主次关系的,我们的高级指针也是一样的。就像是修饰词修饰的主语,指针数组——主体是数组,那么数组内的元素是什么呢?——指针。这时候我们指针数组就分析完毕了。没错,就是这么简单。指针数组就是一个元素为指针的数组。

    🦔但是我们需要特殊记忆的就是指针数组到底应该怎么书写。我们先通过一个例子来预先感受一下指针数组的书写形式:

#include<stdio.h>
int main()
{
	int num1 = 10;
	int num2 = 20;
	int num3 = 30;
	int* pa1 = &num1;
	int* pa2 = &num2;
	int* pa3 = &num3;
	//将多个指针变量作为元素构建一个数组,如下是一个指针数组的书写形式
	int* arr[3] = { pa1,pa2,pa3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		//arr[i]通过下标找到相应的指针变量进行解引用得到不同的值
		printf("%d ", *arr[i]);
	}
	return 0;
}

    🦔我们肯定注意到了指针数组的书写形式似乎有些奇奇怪怪,没关系我们先来一点一点进行拆解。(更奇怪的指针还在后面呢!)还记得我们在介绍二级指针的时候所用的方法吗?(有括号就从括号里面的内容进行解读,没有括号的话就从数组名开始按照优先级进行解读)int* arr[3] 我们可以看到的是在指针数组的书写当中没有括号,所以我们需要做的就是从变量名开始解读:由于中括号 [ ] 的优先级高于 * 所以我们就需要先将变量名和 [ ] 结合,所以我们的变量表示的就是一个数组,数组当中有三个元素,那么数组中的元素类型是什么样的呢?这就需要我们向前进行寻找:找到的就是 int * 所以我们的数组中每个元素的类型就是 int * 的指针。

    🦔在解析完指针数组之后相信大家对于指针数组的用法会很好奇,那我也就直接直白的通过几个例子向大家介绍一下指针数组的使用方法好了。

    🐀指针数组的用法介绍

    🦔指针数组单独使用的意义其实并不大,但是要是结合其他指针进行使用的话就会出现很神奇的效果,在这里我们将指针数组和函数指针相结合,书写一个计算器程序。让我们想象一下,假如要是书写一个多功能可选择的计算器应该怎么办?利用 case 语句?然后输入一个值就进行相应的函数调用。这样书写也不是不行,只不过是会出现大量的代码冗余。那么有没有一种效果可以代替switch 语句直接进行函数的选择调用呢?这就利用到了我们的指针数组了。程序效果如下:

#include<stdio.h>
#include<assert.h>

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)
{
	assert(y != 0);
	return x/y;
}
void meun()
{
	printf("*************************************\n");
	printf("***  1.add          2,sub         ***\n");
	printf("***  3.mul          4div          ***\n");
	printf("***  0.退出                       ***\n");
	printf("*************************************\n");
}
int main()
{
	int (*pa[4])(int, int) = { add,sub,mul,div };
	int input = 0;
	do
	{
		meun();
		scanf("%d", &input);
		printf("%d\n", pa[input - 1](3, 5));
	} while (input);
	return 0;
}

     🦔就像是我们上面的程序那样——我们将一个函数指针放在一个数组中(函数指针的书写以及函数指针数组的书写参考下面的博客内容)之后我们就可以通过定义一个变量,向变量中传值的方式进行函数的选择。是不是感觉一下子就变得很高级了呢?别着急更高级的还在后面呢!

    🐫2.数组指针

    🦌数组指针的介绍以及使用

    🐆相信看到这里不少小伙伴们都会头疼不已,为什么刚送走一个指针数组又出来了一个数组指针,像绕口令,脑筋急转弯似的。我们按照上面的办法再来试着将这个难题解决。显而易见的是数组指针,主体是指针,那么我们指针指向的类型是什么呢?没错就是一个数组。通常我们在使用数组指针的时候都会配合二级指针进行使用。我们接下来通过一个例子进行i进一步的了解:

#include<stdio.h>

//利用指针数组传参进行一个二位数组的打印
void print(int(*pa)[5],int row,int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			printf("%d ",*(*(pa+i)+j));
		}
		printf("\n");
	}
}
int main()
{
	//先创建一个二维数组
	int arr[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
	print(arr,2,5);
	return 0;
}

     🐆由于我们在数组传参传输的是数组中第一个元素的地址,我们可以将我们的二维数组看成是一个有两个元素的数组,数组中的元素类型又是一个数组,也就是将数组的形式写成:                int arr[ 2 ] [ 5 ] = { {1,2,3,4,5} , {6,7,8,9,10} } ; 那么我们第一个元素的内容就可以看成是第一个数组:即 {1,2,3,4,5} 。那么我们要想用一个指针进行接收这个实参,那么我们形参的类型就需要定义为一个数组指针类型。我们可以将数组指针中的内容第一次进行解引用得到 {1,2,3,4,5},之后再进行进一步调整之后再次进行解引用得到具体的元素内容进行打印。

    🐆数组指针的书写形式就像是上图一样,为了便于理解我们来一步步分析一下数组指针的书写形式:首先数组指针主体是一个指针,那么我们的变量名就需要先跟 * 结合,即(*pa)表示变量的主体内容是一个指针,接着往外查找指针所指向的元素的类型——根据优先级先和 [ ] 结合,表示这个指针指向的变量是一个数组,数组的每个元素的类型再往前查找就只剩下剩下的 int 了。那么我们的分析也就结束了。int(数组的元素类型)(*pa)(表示主体是指针)[ 4 ] (表示指针指向的数据类型是一个数组,数组有四个元素)。

    🐆那么我们的数组指针的介绍和使用方法就一并介绍完毕了。那么单独的指针类型就还只剩最后一个——函数指针了。那么让我们赶紧来认识一下什么是函数指针以及怎样使用函数指针吧!

    🦮3.函数指针

    🐈函数指针的介绍

    🐩函数指针顾名思义就是一个函数的指针,我们同样可以利用上面的主次关系分析方法进行分析:首先主体部分就是指针,指针所指向的类型就是一个函数。我们可以利用函数指针进行调用特定的函数,(回调函数)或者在函数中调用其他函数。废话少说,再说就变成绕口令了,那么接下来我们就来通过示例进行认识函数指针吧!

int Add(int x, int y)
{
	return x + y;
}
#include<stdio.h>
int main()
{
	int (*pa)(int, int) = &Add;
	printf("%d", pa(3, 5));
	return 0;
}

     🐩一般指针相关的分析方式都是相通的。所以我们可以按照我们之前的步骤进行再次分析:函数指针,主体是指针,所以需要先用括号将 * 和变量名结合。表示主体是一个指针。之后继续分析,向外查找我们会发现:又出现了一个括号,(表示函数)括号里面的内容是两个 int ,int 那么就代表我们这个指针指向的内容是一个函数,函数的形参为两个 int 类型的数据。那么函数的返回类型是什么呢?我们继续查找,发现只剩下最前面的一个 int ,那么我们的函数的返回值类型就为int 。在想要通过函数指针调用函数的时候就可以通过直接在指针后面的括号里传参的方式进行函数调用就可以了。那么到此我们的函数指针也就分析完毕了,接下来我们来体会一下函数指针正式的用法。

    🐈函数指针的使用介绍

    🐩就像是上面我们在指针数组的程序写到的那样,我们既可以将我们的函数指针放到数组中进行函数的间接调用也可以通过向函数中传参的形式将函数作为参数传递给另一个函数达到我们预期中的效果。那么我们就通过程序来了解函数指针的使用吧。

#include<stdio.h>
#include<assert.h>

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)
{
	assert(y != 0);
	return x/y;
}
void meun()
{
	printf("*************************************\n");
	printf("***  1.add          2,sub         ***\n");
	printf("***  3.mul          4div          ***\n");
	printf("***  0.退出                       ***\n");
	printf("*************************************\n");
}
int calc(int(*pa)(int, int),int x,int y)
{
	return pa(x, y);
}
int main()
{
	int input = 0;
	do
	{
		meun();
		printf("请输入你想选择的计算方式:");
		scanf("%d", &input);
		printf("请输入你要进行计算的数据:");
		int x = 0, y = 0;
		scanf("%d %d", &x, &y);
		switch (input)
		{
		case 1:
			printf("%d\n", calc(add, x, y));
			break;
		case 2:
			printf("%d\n", calc(sub, x, y));
			break;
		case 3:
			printf("%d\n", calc(mul, x, y));
			break;
		case 4:
			printf("%d\n", calc(div, x, y));
			break;
		default:
			printf("输入错误请重新输入。");
			break;
		}
	} while (input);
	return 0;
}

     🐩通过上面的程序我们可以发现想要向函数中将函数作为参数传递给另一个函数,只要学好指针,指针可以做你想做的任何事情。接下来我们来认识一些比较复杂的指针搭配吧!

    🦘能力提升复杂指针的解析:

    🐫1.函数指针数组

    🦖就像是我们上面所写到的那样:函数指针数组就是将函数指针放到数组中进行使用,以达到快速调用函数并且缩短代码长度的作用。那么如何写出一个函数指针数组呢?我们就来一步一步进行深入的分析。还记得我们上面说到的方法吗?就是从变量名开始由主体依次深入的模式。那个方法在长指针分析也同样适用,就比如:int (*pa[4])(int, int)  我们这段代码的分析:先从我们的变量名开始:由于 pa 没有括号结合,所以根据优先级规律,pa 会先和我们的 [ ] 结合。表示我们的 pa 变量表示的是一个数组。之后数组中的元素类型就是剩下的部分:(将我们已经分析过的部分拿走)int (*)(int, int) 这样的格式很容易就发现这是一个函数指针类型。所以我们就只找到了数组中元素的类型是一个函数指针。那么这样我们的函数指针数组就分析完毕了。

    🦕2.数组指针数组

    🦆和上面的函数指针同样的道理,我们的数组指针数组顾名思义就是将我们的数组指针放到一个新的数组当中存储形成的新的指针。我们根据上面的思路进行重新分析:int (*pa[3])[5];我们的变量名同样的没有被括号所约束,所以我们的变量会先和 [ ] 结合表示一个数组,之后拿走我们已经分析过的部分就只剩下了 int(*)[5]表示我们数组中元素的类型是一个数组指针,数组指针指向的数组有五个元素。我们数组指针数组中装的有三个数组指针。

    🐧3.函数指针数组的指针

    🦥在第一个函数指针数组的基础上我们可以深入一下——了解分析一下函数指针数组的指针。是不是已经开始套娃了?哈哈哈,别着急这是我们可能用到的最后一层。同样的道理我们先来看一下函数指针数组的指针的书写形式:int(*(*pa)[3])(int,int); 我们根据我们的逻辑依次进行分析:第一步我们的变量由于括号和我们的*结合表示我们的变量是一个指针然后拿走分析过的部分剩下的部分就是指针所指向的变量的类型为 int(*   [3])(int,int); 这就是我们上面所书写到的函数指针数组。(空白处先和 [ ] 结合表示一个数组,之后数组的元素类型表示一个函数指针)所以将我们分析的结果结合到一起就得到了我们最后的答案——函数指针数组的指针。

    🦡那么上面我们指针的分析也就结束了。希望可以帮到大家。那么祝大家天天开心,学业有成。

    

    

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

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

相关文章

Linux0基础入门:初识shell脚本编程

初识脚本编程到目前为止我们已经知道了 Linux 系统和命令行的基础知识&#xff0c;是时候开始编程了。本章讨论编写 shell 脚本的基础知识。在开始编写自己的 shell 脚本大作前&#xff0c;你必须了解这些基本概念。 使用多个命令到目前为止&#xff0c;你已经了解了如何使用 s…

Revit连接处理:阳台扶手和楼梯扶手,墙和梁

一、Revit中阳台扶手和楼梯扶手的连接处理 如图&#xff0c;有一些阳台扶手和楼梯扶手连接的地方&#xff0c;连接处需要进行处理。 1.在楼板合适的边缘处先画出楼梯 (1)单击“楼梯” (2)在楼梯类型属性对话框中修改楼梯属性 (3)绘制楼梯 为了定位方便、准确&#xff0c;首先要…

重塑底层逻辑,涅槃重生继续远航

背景介绍 从贫困县爬出来本硕均为211学校&#xff0c;在机械专业学习7年&#xff0c;有4年的时间热衷于编程学习。因此一路跨行到IT行业。 履历介绍 从毕业后一直在AI算法行业研究&#xff0c;呆过初创公司&#xff0c;目前在上市公司上班。尝尽IT的苦也吃过IT的甜。从毕业一…

【jQuery】常用API——jQuery效果

jQuery 给我们封装了很多动画效果&#xff0c;最为常见的如下&#xff1a;一、显示隐藏切换效果1. 显示语法规范 show([speed,[easing],[fn]]);显示参数&#xff1a;&#xff08;1&#xff09;参数都可以省略&#xff0c; 无动画直接显示。&#xff08;2&#xff09;speed&…

el-date-picker日期时间组件 报 placement 警告的解决方法

在使用el-date-picker组件时报这个警告&#xff0c;虽然不影响页面&#xff0c;但一打开页面跳出来一堆错误警告&#xff0c;实在受不了 解决办法&#xff1a;加上以下一行即可

无序字母对 -- 欧拉回路

洛谷&#xff1a;P1341 无序字母对题目描述前置知识欧拉路径定义判断是否为欧拉图思路code参考题目描述 题目描述 给定 n 个各不相同的无序字母对&#xff08;区分大小写&#xff0c;无序即字母对中的两个字母可以位置颠倒&#xff09;。请构造一个有 (n1) 个字母的字符串使得每…

同源、跨域的概念与实现

本文将结合周老师的讲义对同源与跨域这一前端经典问题进行系统的总结、整理。一起来坐牢&#xff0c;快&#xff01; 1. 同源限制 1.1 历史背景 - 含义的转变 1995年&#xff0c;同源政策由 Netscape 公司引入浏览器。目前&#xff0c;所有浏览器都实行这个政策。 最初&…

爬虫代理Scrapy框架详细介绍4

Scrapy 框架 Scrapy实例 下载安装 pip install scrapy Hello World 创建工程 在 cmd 下切换到想创建 scrapy 项目的地方&#xff0c;然后使用命名 scrapy startproject tutorial 注&#xff1a;tutorial 为工程名 然后就会发现在当前位置会多出一个文件夹&#xff0c;名字是 tu…

C++——map|set介绍

目录 关联式容器 set set的构造 set的迭代器 set的容量 set修改操作 equal_range multiset map map的构造 map的迭代器 map的容量与元素访问 map测试 关联式容器 在初阶阶段&#xff0c;我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、…

Linux内核权限提升漏洞

SSRF检测的一些思考 DNS平台没有立刻收到请求&#xff0c;是在之后的某个时间段收到了不同的请求信息&#xff0c;这至少表明了一点&#xff0c;此处存在有无回显的SSRF&#xff0c;虽然想要证明有更大的危害比较困难&#xff0c;但是至少说明了存在有SSRF的风险&#xff0c;所…

Maven 命令之将本地 Jar 包安装到 Maven 本地仓库

1、前言 Maven 是 Java 平台下的一款项目构建和依赖管理的自动化管理工具。 通过 Maven 远程仓库地址我们可以方便的管理 Jar 依赖包&#xff0c;但是在实际项目中有时候存在远程仓库中没有的 Jar 包&#xff0c;我们在项目中又必须要使用它&#xff0c;那就需要把本地 Jar 添…

HC-SR04超声波传感器使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、关于HC-SR04二、使用步骤1.确保驱动已经安装2.安装GPIO工具3.安装GPIO的Python支持4.Python3代码总结前言 最近在做一个项目&#xff0c;需要用到超声波传感…

誉辰智能拟科创板上市:欲募资4亿元,毛利率、研发费用率均下滑

近日&#xff0c;深圳市誉辰智能装备股份有限公司&#xff08;下称“誉辰智能”&#xff09;在上海证券交易所更新招股书&#xff08;申报稿&#xff09;&#xff0c;披露时间为2023年1月7日&#xff0c;准备在科创板上市。据贝多财经了解&#xff0c;誉辰智能曾于2022年6月29日…

解决SpringBoot项目整合Sharding-JDBC后启动慢的问题

一、问题描述线上某一项目以jar包的方式发布&#xff0c;需要健康检查&#xff0c;若15次健康检查页面状态均为非200则健康检查失败&#xff0c;就会导致tomcat服务加载失败。前几天版本迭代&#xff0c;发布该项目时&#xff0c;因最后一次健康检查的时间比启动完成时早&#…

练习时长两年半的tcp三次握手

1、TCP是什么&#xff1f;TCP是面向连接的协议&#xff0c;它基于运输连接来传送TCP报文段&#xff0c;TCP运输连接的建立和释放&#xff0c;是每一次面向连接的通信中必不可少的过程。TCP运输连接有以下三个阶段&#xff1a;建立TCP连接&#xff0c;也就是通过三报文握手来建立…

Oracle打补丁

oralce打补丁 打补丁前提&#xff1a; 一、备份数据库 二、将oracle服务全部停掉 1、查看opatch的版本号 1.1、环境变量配置ORACLE_HOME 1.2、运行opatch version命令&#xff0c;查看opatch的版本号 备注&#xff1a;网上查看opatch的版本号对应的oracle是否匹配&#xff0c;…

【分治策略】查询中位数最接近点对

查询中位数给定线性序集中n个元素和一个整数k 【k(n1)/2】&#xff0c;要求找出这n个元素中第k小的元素&#xff0c;即找中位数。线性序列没有排序,没有重复值。已知快速排序划分时一个划分基准数的位置在确定后&#xff0c;在之后排序中是不会变的。利用此特性&#xff0c;以下…

dataphin如何使用zip文件,离线安装python第三方包?

好久没写文章啦&#xff0c;快过年了啦&#xff0c;打工人要回家啦 背景介绍&#xff1a; 每次在dataphin里使用pandas的时候&#xff0c;都要pip install pandas。dataphin需要下载pandas安装包&#xff0c;比较费时。总而言之&#xff0c;这种方式慢。 所以我要在dataphin的…

【手写 Vue2.x 源码】第十四篇 - 生成 ast 语法树 - 模板解析

一&#xff0c;前言 上篇&#xff0c;主要介绍了生成 ast 语法树-正则说明部分&#xff0c;涉及以下几个点&#xff1a; 简要说明了 HTML模板的解析方式对模板解析相关正则说明和测试 本篇&#xff0c;生成 ast 语法树-代码实现 二&#xff0c;模板解析 模板解析的方式&…

22. 听说你想要用爬虫采集我的手机号?哎 ~ 我展示用的是图片

本篇博客我们实现图片渲染手机号码案例&#xff0c;用于防止爬虫直接采集文字信息。 爬虫训练场 本案例实现的效果如下所示 文章目录bootstrap5 实现名片样式卡片补充数据生成逻辑生成用户 5 个汉字的昵称调用头像 API&#xff0c;生成图片将手机号码生成图片bootstrap5 实现名…