有希带你深入理解指针(3)

news2024/12/24 10:06:25

前言

本篇文章是对指针知识的进一步讲解,如果对部分知识有不了解的地方可以移步前文进行学习!

在这里插入图片描述

1.字符指针变量

该内容我们在前面的文章中已提到过,想必大家对它应该不陌生吧!这里我们会对它进行详细的介绍。
一般情况下,我们是这样使用的:

int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'w';
	return 0;
}

其实还有一种用法,如下:

char* pc = "abcdef";

此时,大家对这种写法可能不太理解,我们用一个例子进行讲解。

char arr[] = "abcdef";
char* p = arr;

这里我们创建了一个字符数组额,里面放了a b c d e f \0。之后我们将数组名(首元素的地址)放在了p中。arr数组里面的元素在内存中的存放是连续的,并且数组的内容是可以改变的。

现在我们回到要讲解的写法,用下图理解:
在这里插入图片描述
该写法是将字符串中首字符的地址赋给p。在上图中,我在每一个元素下添了下标,大家可能会想和数组一样吗?
这里其实是差不多的,我们都可以用下标访问里面的元素。在这里插入图片描述
注意:
char* pc = "abcdef"这种写法是不太严谨的,我们在这里放的是常量字符串,常量字符串是不能被修改的,但是我们现在对pc没有限制,如果我们进行修改,程序会崩溃。如图:

int main()
{
	char* pc = "abcdef";
	*pc = 'w';
	return 0;
}

在这里插入图片描述
现在我们需要限制修改,只需在char* pc前面加const就行。

2.数组指针变量

我们可以通过画图理解:
在这里插入图片描述
注意这里我们需要存放的是数组指针,即数组的地址。我们前面提到过数组的地址用&arr来表示,而不是arr或&arr[0],它们表示的是首元素的地址。下面我们用代码进行演示写法(这里我们用整型数组为例):

int main()
{
	char ch = 'w';
	char* pc = &ch;

	int num = 10;
	int* pi = #

	int arr[10] = { 0 };
	int(*parr)[10] = &arr;

	return 0;
}

这里的parr就是数组指针变量。[10]是不能省略的,指向哪一个数组,数组里面有几个元素,以及元素类型是什么必须表示清楚。

这里我们在拓展不同类型数组指针的写法:

char arr2[10];
char(*parr2)[10] = &arr2;

int* arr3[23];
int* (*arr3)[23] = &arr3;

大家还记得前面一篇文章中我说在后面的文章会介绍&arr的类型是什么吗?现在来了!
我们用例子来理解:

int main()
{
	int arr[10] = { 0 };
	int(*p)[10] = &arr;
	return 0;
}

这里p是数组指针变量,&arr是数组的地址,等号两边相等的话,说明两边的类型是一样的,那p是什么类型呢?
其实去掉名字就是它的类型,即int( * )[10],那么&arr的类型就是int( * )[10]。当然我们可以通过下图帮助理解:
在这里插入图片描述
此时我们引入一个实例来理解:
我们想用数组指针把整个数组打印出来。这里我们把数组指针设为p。p+1则是跳过整个数组,p是一个数组指针变量,我们进行解引用拿到的是整个数组,通过下图我们可以看到 * p的大小:在这里插入图片描述
代码:

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int(*p)[10] = &arr;

	int sz = sizeof(arr) / sizeof(arr[0]);

	for (int i = 0; i < sz; i++)
	{
		printf("%d ",(*p)[i] );
	}
	return 0;
}

运行结果:
在这里插入图片描述
但是这样写有些别扭,我们可以换一种写法。
在这里插入图片描述
这里我将首元素的地址放在p里面,通过p整个指针逐步向后进行访问。这里我们会发现数组指针在该问题下运用比较僵硬,数组指针的比较适合的应用在下个内容会讲到。

3.二维数组传参的本质

假设我们有一个二维数组,我们设计一个函数打印该二维数组的内容,在传参的时候我们需要将二维数组的行和列传过去,注意形参的行可以省略,列不可以省略。
代码演示:

#include<stdio.h>
void Print(int arr[][5], int r, int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}


int main()
{
	int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
	Print(arr, 3, 5);
	return 0;
}

运行结果:
在这里插入图片描述
此时我们很顺畅的写出了代码,那大家有没有想过二维数组传参的本质是什么呢?

在二维数组传参时,实参部分我们写的是数组名,前面我们提到过数组名是数组首元素的地址,既然我们实参传过去的是地址,那么形参就可以用指针变量来接收。

现在我们有个问题需要解决:二维数组的数组名是谁的地址?首元素是谁?

在这里插入图片描述
二维数组的元素是一维数组,即二维数组的每一行就是一个元素。那么二维数组的数组名实际上就是第0行的地址。利用上一部分的内容,我们可以把形参部分改为int ( * p)[5]来接收。

4.函数指针变量

函数指针变量是什么呢?
目前我们知道变量可以取地址,数组可以取地址,那函数可以取地址吗?当然是可以的!
我们现在可以试试看,如图(环境: VS2022 Debug X86):
在这里插入图片描述
现在我们通过实例发现函数的地址是可以取出来的。之前我们说过,数组名是数组首元素的地址,&数组名是数组的地址。那现在我们可能会想函数名是什么?现在我们开始测试。
在这里插入图片描述
通过图片我们发现显示的结果是一样的!可能大家会进行推测,函数名是函数首元素的地址,&函数名是函数的地址。但是并表示这样的,对于函数来说就只有一个地址,那就是函数的地址。即通过&函数名和函数民都可以拿到函数的地址。

现在我们想把函数的地址存起来,那类型应该怎么写呢?这里和数字指针比较类似。

int (*p)(int x, int y) = &Add;

这里的形参是可以省略的,即可以写成(int , int),但是参数的类型和个数是不可以少的。

int (*p)(int , int ) = &Add;

这里的p就是函数指针变量。这里的 * 不是解引用,int( * )(int, int)表示的是函数指针变量类型。这个 * 是给p的,意在提醒它是一个指针变量。

这里我们引入一个例子来帮助大家理解。

int *p,q;

这里的p和q分别是指针和int,所以这里的 * 并不是解引用,只是为了说明p是指针。
但是函数指针变量的作用是什么呢?我们可以利用它调用函数,这里可以多次调用。

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*p)(int , int ) = &Add;

	int ret = (*p)(2, 3);
	printf("%d\n", ret);
	return 0;
}

但是之前我们在调用函数的时候采用的并不是这种方法,而是如下方法:

int ret = Add(2,3);

我们提到过函数名是函数的地址,在原来的方法中,我们直接使用了函数名。而今天我们提到的方法中,我们采用了函数指针来调用,并且进行了解引用,相当于对函数名进行了解引用。我们可以试试不解引用的效果。

在这里插入图片描述
根据结果来看,这里是没有问题的,因为这里的 * 只是起到了提醒的作用,和之前我们讲的数组传参的时候,形参本质上应该是指针,但是我们写成数组比较容易理解。既然这里的 * 只是装饰的作用,写几个效果都是一样的。

现在我们对两个代码进行理解:

	(* (void (*)())0 )();

这里我们将0作为突破口,void( * )()是函数指针类型,像这样的形式我们之前在强制类型转换中见到过,例如(int)3.14。这里是把int型的0强制类型转换为函数指针,即希望0成为函数的地址。此时0和我们之前说的函数指针变量就没有区别了,前面的 * 是对函数的地址进行解引用,对该函数进行调用,最后的()就是传参,因为指向的函数没有参数,所以我们的括号里面没有写东西。0这个地址虽然不允许我们用户使用,但是不影响我们分析代码。

void (* signal(int, void(*)(int))) (int);

这里我们将signal作为突破口,它首先是与后面的()结合,它是一个函数,它有两个参数分别为int和函数指针类型。这里是一个函数声明,因为在函数声明中,我们并不需要写形参的名字 。那现在还剩下函数的返回类型不清楚,去掉我们刚才讲的部分就是返回类型:void ( * ) (int),此时我们发现返回类型是函数指针。
但是这段代码还是比较复杂,我们先学习一个知识,对它进行简化。

typedef关键字

typdef是用来类型重命名的,可以将复杂的类型,简单化。
例如:

typedef unsigned int unit;

int main()
{
	unsigned int num;
	unit numn2;
	return 0;
}

这里我们就不需要写unsigned int 这样比较长的类型,这样写起来就比原理来写的方便。
当然指针类型也可以进行重命名。


typedef int* ptr_t;
int main()
{
	int* p;
	ptr_t p1;
	return 0;
}

这样就行重命名有一个好处:

int* p1, p2;
ptr_t p3, p4;

这里p1是int * 类型的,p2是int类型;p3和p4是int * 类型的。此时避免了我们前文中所说的 * 只给p1这种情况。但是可能有人认为没有必要,现在我们对数组指针进行重命名。

typedef int(*parr_t)[10];
int main()
{
	int arr[10];
	int (*pa)[10] = &arr;
	parr_t pb = &arr;

	return 0;
}

这里的pa是数组指针变量,它的类型是int ( * )[10]。我们使用typedef进行重命名,再创建同样的类型的变量时,就不用写那么一长串类型了。
同理,对于函数指针我们也可以进行重命名。

int Add(int x, int y)
{
	return x + y;
}

typedef int(*pf_t)(int, int);
int main()
{
	int (*pf)(int, int) = Add;
	pf_t pf2 = Add;
	return 0;
}

pf是函数指针变量,类型是int( * )(int,int)。我们使用typedef进行重命名,创建同样类型的变量pf2。

当然进行重命名之后我们不容易看出变量的类型,但是我们可以对代码进行简化。现在我们回到一开始需要简化的那一个代码。
这里我们对void( * )(int)进行重命名。

typedef void(*pf_t)(int);

int main()
{
	//void (*signal(int, void(*)(int))) (int);
	pf_t signal(int, pf_t);
	return 0;
}

这样写之后,这段代码比之前看起来直观。到这里我们的函数指针变量就讲完了!

5.函数指针数组

前面我们学过指针数组,例:

int * arr[5];
char *ch[6];

这里有两个数组,里面分别放了整型指针和字符指针。那我们可以把函数指针也放在数组中吗?答案是可以的!

int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int main()
{
	int(*pf1)(int, int) = Add;
	int(*pf2)(int, int) = Add;
	return 0;
}

这里我们有两个函数,我们现在把这两个函数的地址存起来之后我们发现,它们的类型是一样的。假设我们这里有更多的函数指针变量,并且它们的类型都是一样,我们就会想到把这类的地址存放到一个数组中。此时我们就需要函数指针数组。创建的方法就是从函数指针的基础上改造。如下:

int (*pfArr[4])(int, int) = { Add,Sub };

注意:这里存放的多个函数的地址类型应该是相同的。

现在我们学习完基本的知识之后,我们来讲函数指针的用途。先用一个不用函数指针数组的例子,之后我们加上函数指针数组。
现在我们要写一个计算器的代码。功能如下:

  1. 加法
  2. 减法
  3. 乘法
  4. 除法

我们首先利用do-while循环打印菜单,并在菜单中给出选项,利用switch对各个情况进行处理。while()里面填input就可以控制计算器走到case 0结束这一过程。针对switch里面的各个情况,我们设计进行提醒。
在各个情况中,我们加入对应的功能。如下:

#include<stdio.h>
void menu()
{
	printf("**************************\n");
	printf("****1.add        2.sub****\n");
	printf("****3.mul        4.div****\n");
	printf("******     0.exit   ******\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 main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;

	do
	{
		menu();
		printf("请选择:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &x, &y);
		    ret = Add(x, y);
			printf("%d\n", ret);
		    break;
		case 2:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

此时代码逻辑上是没有大问题的,可能有些小bug,这里不需要过多注意。假设后期我们需要添加一些功能,我们的菜单肯定需要改,当然也需要添加一些函数,switch也需要进行修改。但是有什么办法可以进行简化呢?注意看这些函数的参数和返回类型一模一样。目前我们利用函数名对函数进行调用,此时我没找到利用函数的地址也可以进行调用。知道这些内容之后我们可以对代码进行改造。

#include<stdio.h>
void menu()
{
	printf("**************************\n");
	printf("****1.add        2.sub****\n");
	printf("****3.mul        4.div****\n");
	printf("******     0.exit   ******\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 main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	int (*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };

	do
	{
		menu();
		printf("请选择:\n");
		scanf("%d", &input);
		printf("请输入两个操作数:\n");
		scanf("%d %d", &x, &y);
		ret = pfArr[input](x, y);
		printf("%d\n", ret);
	} while (input);
	return 0;
}

我们利用了函数指针数组,并在里面添加了5个元素,大家会发现这里我添加了0,它的作用是使函数的下标和选项对应上。这样改动之后我们在switch语句中保留一份就可以了。到这里之后我们的代码基本没有问题了,但是还存在一些小问题,如图:
在这里插入图片描述
我们输入9之后,跳出了该选项。我们肯定需要对代码进行优化。我们只需要加上一个if判断一下即可。

#include<stdio.h>
void menu()
{
	printf("**************************\n");
	printf("****1.add        2.sub****\n");
	printf("****3.mul        4.div****\n");
	printf("******     0.exit   ******\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 main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	int (*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };

	do
	{
		menu();
		printf("请选择:\n");
		scanf("%d", &input);
		if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("%d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
			break;
		}
		else
		{
			printf("选择错误,请重新选择\n");
		}

	} while (input);
	return 0;
}

之后我们需要其他功能,只需要写出对应的函数,并在函数指针数组里面添加就行。我们此时就把代码简化了许多。这里的函数指针数组类似一个跳板,我们可以通过下标来找到数组中的函数的地址并对其调用,提供了6一种类似跳转的效果。所以我们一般把这种数组称为转移表。

到这里我们本篇内容就结束了,指针的更多内容请看下一篇blog。如果文章内容有误请大佬在评论区斧正!谢谢大家!
在这里插入图片描述

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

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

相关文章

FPGA开发——IIC实现简单的串口回环

一、概述 在我们进行日常开发时&#xff0c;不管是进行MCU、单片机、还是FPGA&#xff0c;都会使用到IIC通信协议。采用串行总线可以简化系统硬件结构、减小系统体积、提高系统可靠性。常 用的串行总线有单总线&#xff08;1-Wire Bus&#xff09;、IIC&#xff08;Inter-Integ…

Codeforces Round 926 (Div. 2) C. Sasha and the Casino (博弈论*1400)

这里的意思是想让我们求得是否是能够实现不停地无上限的赚钱。 这里注意避开一个思维误区&#xff0c;如果你想的是前x次一直用1枚硬币然后吃第x1次保底&#xff0c;那么就是错误的。你应该考虑到如果前x次里面出现了胜利呢&#xff1f;这时候你拿着一枚硬币根本赚不回本。 所…

全志H616系统启动和登录

一、系统启动 刷完机烧入镜像&#xff0c;直接用MobaXterm软件串口登陆 约定固定的波特率115200。 默认登录&#xff1a; 用户&#xff1a;orangepi 密码&#xff1a;orangepi 或用户&#xff1a;root 密码&#xff1a;orangepi 在输入密码时…

YOLO 单目测距:原理、方法与代码

一、原理 单目测距的一个常见方法是假设物体的尺寸已知。通过测量物体在图像中的高度&#xff08;或宽度&#xff09;&#xff0c;并结合物体的实际高度&#xff08;或宽度&#xff09;&#xff0c;最简单的一种方式就是利用相似三角形的原理来计算物体的距离。 二、相似三角…

使用深度学习来进行击剑动作识别的裁判工作

在击剑比赛中&#xff0c;当双方几乎同时击中对方时&#xff0c;记分板两边都会亮起。这时裁判需要决定哪一方得分。一般而言&#xff0c;谁更主动或控制了局势就会得分。我尝试训练了一个模型来辅助裁判做这样的判断&#xff01;目前该模型在花剑测试集上的准确率大约为60%&am…

Vue开发者工具安装详细教程

欢迎大家订阅【Vue2Vue3】入门到实践 专栏&#xff0c;开启你的 Vue 学习之旅&#xff01; 文章目录 前言一、下载二、安装三、调试 前言 Vue 是一个框架&#xff0c;也是一个生态&#xff0c;其功能覆盖了大部分前端开发常见的需求。本文详细讲解了 Vue 开发者工具的安装。 …

ES7.17.5 float类型 terms带来的隐患

背景 1.用户在mapping中加一个字段 testid&#xff0c;结果写数据的时候使用 testId&#xff0c;同时也没有strict限制动态mapping&#xff0c;只是使用了默认的 true&#xff0c;即允许动态生成mapping 2.动态生成的字段 testId 被识别成了 float&#xff0c;用户为了方便&a…

【Netty 一】

Netty是什么 Netty 是一个高性能、异步事件驱动的 NIO 框架&#xff0c;基于 JAVA NIO 提供的 API 实现。它提供了对 TCP、 UDP 和文件传输的支持&#xff0c;作为一个异步 NIO 框架&#xff0c; Netty 的所有 IO 操作都是异步非阻塞 的&#xff0c; 通过 Future-Listener 机制…

ssrf漏洞之——漏洞复现

漏洞介绍 SSRF漏洞&#xff1a;SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由恶意访问者构造url&#xff0c;由服务端对此url发起请求的一个安全漏洞。 漏洞原理 SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能&#xff0c;并且没有对目…

Autosar(Davinci) --- 创建一个Implementation Data Types

前言 这里我们讲一下如何创建一个Implementation Data Types&#xff08;IDT) 一、什么是IDT 二、如何创建一个IDT 鼠标右键【Implementation Data Types】,选择【new Type Reference...】 起一个名字【IdtDoorState】&#xff0c;Data Types选择【boolean】&#xff0c;这里…

RFID光触发标签应用于制造业供应链管理的应用与探索

制造业作为国民经济的支柱产业&#xff0c;其供应链管理的复杂性和重要性日益凸显&#xff0c;在全球化竞争的背景下&#xff0c;企业需要更高效、更精准、更智能的供应链解决方案来满足市场需求&#xff0c;提高客户满意度&#xff0c;降低运营成本&#xff0c;RFID光触发标签…

【mysql】mysql的卸载和安装

mysql的卸载 mysql是否安装&#xff1a; 首先我们先来看看mysql是否安装&#xff1a; 快捷键winR输入cmd&#xff0c;进入命令输入框 输入mysql --version 查看mysql的版本 如果出现了mysql的版本就说明你已经安装了 系统用户root -p就是输入密码所以代码如下 mysql -ur…

AI大模型编写多线程并发框架(六十一):从零开始搭建框架

系列文章目录 文章目录 系列文章目录前言一、项目背景二、第一轮对话-让AI大模型理解我们的诉求二、第二轮对话-优化任务处理方法和结果处理方法三、参考文章 前言 在这个充满技术创新的时代&#xff0c;AI大模型正成为开发者们的新宠。它们可以帮助我们完成从简单的问答到复杂…

模拟实现STL中的unordered_map和unordered_set

目录 1.unordered_map和unordered_set简介 2.unordered_map和unordered_set设计图 3.迭代器的设计 4.哈希表的设计 5.my_unordered_map和my_unordered_set代码 1.unordered_map和unordered_set简介 unordered_map和unordered_set的使用非常类似于map和set&#xff0c;两…

【Linux】日志函数

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 引言日志内容日志等级日志函数的编写函数原型参数说明功能描述使用场景示例代码 引言 日志在程序设计中扮演着至关重要的角色&#xff0c;它不仅是程序运行情况的记录者&#xff0c;还是问题诊断、性…

【机器学习】智驭未来:机器学习如何重塑现代城市管理新生态

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀目录 &#x1f50d;1. 引言&#xff1a;迈向智能城市的新时代&#x1f4d2;2. 智驭交通&#xff1a;机器学习在智能交通管理中的应用&#x1…

仿Muduo库实现高并发服务器——LoopThreadPool模块

这个模块需要具备那些基础知识。 线程创建相关操作&#xff0c;锁&#xff0c;条件变量。 设置线程数量&#xff1a; _thread_count 是线程池中&#xff0c;记录线程数量的成员。 创建线程池&#xff1a; 上图就是线程池的创建&#xff0c;将线程与EventLoop对象 通过数组下…

关于嘉立创eda中同一个项目下多个原理图是否独立

嘉立创项目底下&#xff0c;如果你新建了多张原理图&#xff0c;如下 我发现&#xff0c;多张原理图是互相连接的&#xff0c;所以命名是不能重复的 多页原理图 | 嘉立创EDA标准版用户指南https://docs.lceda.cn/cn/Schematic/Multi-Sheet/index.html 上面是嘉立创原文介绍 综…

豆瓣评分7.9!世界级讲师耗时5年整理出的Python学习手册!

Python是一门流行的开源编程语言&#xff0c;广泛用于各个领域的独立程序与脚本化应用中。它不仅免费、可移植、功能强大&#xff0c;同时相对简单&#xff0c;而且使用起来充满乐趣。从软件业界的任意一角到来的程序员&#xff0c;都会发现Python着眼于开发者的生产效率以及软…

编程仙尊——深入理解指针(2)

目录 4.const修饰指针 4.1const修饰变量 5.指针运算 5.1指针-整数 5.2指针-指针 5.3指针的关系运算 6.assert断言 4.const修饰指针 4.1const修饰变量 在编程中&#xff0c;为了防止代码在运行过程中变量的内容意外改变&#xff0c;可以使用const函数&#xff0c;对变量…