深入理解并打败C语言难关之一————指针(5)(最终篇)

news2024/11/21 2:23:08

前言:

  仔细一想,小编已经把指针的大部分内容都说了一遍了,小编目前有点灵感枯竭了,今天决定就结束指针这一大山,可能很多小编并没有提到过,如果有些没说的小编会在后续博客进行补充道,不多废话了,下面让我们开启今天的指针之旅吧!


目录:

1.函数指针数组

1.1.函数指针数组是什么

1.2.函数指针数组的应用

2.sizeof操作符和strlen函数的辨析

2.1.sizeof操作符

2.2.strlen函数

2.3.二者的比较

3.本节代码展示


正文:

1.函数指针数组

1.1.函数指针数组是什么

  前面我们已经学习了函数指针,数组指针,指针数组等内容 ,看到这读者朋友已经很疑惑了,居然还有函数指针数组这个东西,同样的,我们类比一下,存放着整型的是整型数组,存放指针的是指针数组,以此类推,存放着函数指针的数组,自然就是函数指针数组了,所以,这个很长的玩意就是一个数组,不过存放的内容是函数指针罢了,我们需要学会其的创建方式,我们先把代码放到下面供读者阅读:

int (*p1[3])   ();

  首先,p1会先和[]进行结合,表示这个是一个数组,它的类型就是int (*) ()函数指针类型,所以它存放的类型是函数指针,其实这个稍微有点小绕,所以小编决定同样也用图文的方式来给读者朋友们进行更好的解释:

  为了让各位区分一些指针的名称,小编下面整理了前几篇博客写过的一些指针或者数组来让大家区分:

1.指针数组:

int *p1[3];  //中括号里面的个数看题目,这里为了好看统一用3

2.数组指针(和1进行区分):


int (*p2)[3];

3.函数指针:

int (*p3)( )

4.函数指针数组:

int (*p4[])()  //这个对比下来应该是最复杂的了纯纯

  以上便是小编所说过的一些名称,这些要记住,其中前三个都分别放在了小编写过的(3)和(4)里面,感兴趣的读者朋友可以去看看(下面放上了想关链接):深入理解并打败C语言难关之一————指针(3)-CSDN博客 深入理解并打败C语言难关之一————指针(4)-CSDN博客

  既然我们已经讲完了函数指针数组如何进行创建,那么我们下面就要进行应用了,对于其的应用我们用简易计算机为例子,下面让我们进入下一环节,应用! 

1.2.函数指针数组的应用  

  我相信各位学过了前面的知识,已经会使用了函数和一些重要的语句,那么我们可以通过用这些知识来制作一个简易的计算机(仅仅针对整型),下面我们先来一个不用函数指针数组的简易计算机:

  计算机小编相信读者朋友们都用过,里面的功能有很多,下面我们就以正常算法的加减乘除为例子来进行代码的写,首先我们要先有着菜单页,这个可以模仿小编之前写过的扫雷游戏的菜单页,下面直接代码展示:

void menu()
{
	printf("***********简易计算机************\n");
	printf("*****1.加法***********2.减法*****\n");
	printf("*****3.除法***********4.乘法*****\n");
	printf("*****0.退出**********************\n");
	printf("**********************************\n");
	printf("*********************************\n");
}

  做完菜单后,之后我们就要进入选择环节了,这部分内容其实和扫雷很像,但为了考虑部分读者朋友没有看过那篇文章,小编再来说一下,首先我们可以选用do while语句来一直循环,因为这个语句总会先循环一次在判定条件,之后我们再用switch语句来判定想选哪个选项,之后我们在每次的条件下开始输入我们想要的数,然后进行函数的传参,下面是代码展示:

int main()
{
	menu();             //对于菜单函数的引用
	int i = 0;             //这一个是负责选哪一个运算方式的
	int a = 0;               //这一个和下面那个都是要进行运算的数
	int b = 0;
	do                    //进入do while 循环(先循环一次然后看循环条件)
	{
		printf("请选择你想要的算法:");                //这个是让你选算法的
		scanf("%d", &i);                      //这个是要输入的字符
		switch (i)                    //这一些跟菜单上数字对应的方式一一对应
		{
		case 0 :                      
			printf("好的下次光临\n");
			break;
		case 1 :
			printf("请选择您要的操作数:");
			scanf("%d %d", &a, &b);
			int c = Add(a, b);
			printf("%d\n", c);
			break;
		case 2 :
			printf("请选择您要的操作数:");
			scanf("%d %d", &a, &b);
			int d = div(a, b);
			printf("%d\n", d);
			break;
		case 3 :
			printf("请选择您要的操作数:");
			scanf("%d %d", &a, &b);
			int f = mul(a, b);
			printf("%d\n", f);
			break;
		case 4 :
			printf("请选择您要的操作数:");
			scanf("%d %d", &a, &b);
			int h = mul(a, b);
			printf("%d\n", h);
			break;
		default :
			printf("您选错了请重新选择:");
			break;
		}
	} while (i);
	return 0;
}

  然后我们就要撰写函数部分了,这部分的内容其实很简单,就是对照着自己想要的算法传参就好了,下面直接展现代码:

int Add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int div(int x, int y)
{
	return x / y;
}
int mul(int x, int y)
{
	return x * y;
}

   之后我们就写完了这个代码,其实乍一看,这么写是很正常的,但是如果仔细一看,发现其实这个代码是很复杂的,首先它的代码量是很复杂的,这个是可以很快看出来的,并且这个代码感觉有的部分显得很赘余,就比如每次输两个数,光这个操作就已经浪费了很多行了,所以我们可以试想一下,可不可以通过一个总函数,这个函数就已经负责了两个数的输入已经算法的选择,每次我们直接通过调用这个函数就好了,所以我们通过调用函数指针的方式就可以做到对这个代码的优化,下面是这个代码的第一次优化:

void oay(int(*p)(int x, int y))        //这里是通过在函数里面调用函数来实现
{        //此时上面这个函数里面放置着被调用的算法,可以看作函数指针的一个应用
	int a = 0;
	int b = 0;
	printf("请选择您想要运算的两个数:");
	scanf("%d %d",&a ,&b );
	int len = (*p)(a, b);
	printf("%d\n", len);
}

  此时已经让这个代码做到第一次优化了,此时这个优化做到了对于每次选择一个算法都要写一遍scanf的不足,这里其实后者函数是由名字的,这个可以叫做回调函数!不过,此时这个算法其实还有可以提升的空间的,就比如,我们可以利用我们刚学的函数指针数组的内容,咱们把每个算法函数的指针放到函数指针数组里面吗,然后通过输入算法来调用数组中的元素从而调用想要的算法,此时我们仅仅需要用到if语句判断我们所选择的算法是否是我们数组内部就好,废话不多说,下面是代码展示:

int main()
{
	menu();
	int i = 0;
	int a = 0;
	int b = 0;
	int(*p[5]) (a,b) = {0,add,sub,div,mul};      //md函数指针数组的名字别写错了
	do
	{
		printf("请选择您想要的算法:");
		scanf("%d", &i);
		if (i <= 4 && i >= 1)
		{
			printf("请输入您想要的数字:");
			scanf("%d %d", &a, &b);
			int len = (*p[i])(a, b);         //这相对于函数指针数组的数进行引用了,
			printf("%d\n", len);
		}
		else if (i == 0)
		{
			printf("期待您的下次游玩");
			break;
		}
		else
		{
			printf("您输错了请重新输入:");
		}
	} while (i);
}

  这个就是这个代码最主要的优化,可能很多人看了都会是下面这个表情:

   所以算法是有千万种的,一个题目往往可以有很多的算法,各位读者朋友们一定要学会举一反三,这对于后面编程的学习有很大的帮助,另外其实最后一个代码还是有个好名字的,它叫做转移表!下面不多废话,进入本节的一个小重点,sizeof操作符与strlen函数的辨析!

 

2.sizeof操作符和strlen函数的辨析

2.1.sizeof操作符

  这个操作符小编在前面说过,这个是计算数组长度的运算符,不过sizeof和数组(这里用arr)有个特殊的关系,小编在之前的文章说过,sizeof(arr)运算的是数组整个的长度,这个特例大家一定要记住,以后在小编在出辨析题的时候可能会有坑哦~

2.2strlen函数

  这个函数小编在之前也说过,这个函数的作用是计算字符串中\0之前的字符个数的,这里应该暂时没有特殊情况,下面我们进入sizeof操作符和strlen的比较环节,对了,这里放一张strlen库函数的相关图让读者阅读:

2.3.二者的比较

  其实二者本质就有很大的区别,前者是一个操作符,后者是一个库函数,需要包含在头文件<string.h>;前者是计算操作符所占内存的大小,单位是字节,后者是计算字符串中字符的个数;前者并不关注存的数据,给它数据它就闷头计算,后者如果一直找不到\0,如果找不到的话会一直找,可能出现越界现象!所以说二者是有着明显的区别的,读者朋友们一定要好好的区分!下面就放上面计算机的代码:

 

3.本节代码展示:

1.优化前的简易计算机代码:

void menu()
{
	printf("******************************\n");
	printf("***********简易计算器*********\n");
	printf("*******1.加法*****************\n");
	printf("*******2.减法*****************\n");
	printf("*******3.除法*****************\n");
	printf("*******4.乘法*****************\n");
	printf("*******0.退出*****************\n");
}
int Add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int div(int x, int y)
{
	return x / y;
}
int mul(int x, int y)
{
	return x * y;
}
int main()
{
	menu();             
	int i = 0;             
	int a = 0;               
	int b = 0;
	do                   
	{
		printf("请选择你想要的算法:");               
		scanf("%d", &i);                      
		switch (i)                  
		{
		case 0 :                      
			printf("好的下次光临\n");
			break;
		case 1 :
			printf("请选择您要的操作数:");
			scanf("%d %d", &a, &b);
			int c = Add(a, b);
			printf("%d\n", c);
			break;
		case 2 :
			printf("请选择您要的操作数:");
			scanf("%d %d", &a, &b);
			int d = div(a, b);
			printf("%d\n", d);
			break;
		case 3 :
			printf("请选择您要的操作数:");
			scanf("%d %d", &a, &b);
			int f = mul(a, b);
			printf("%d\n", f);
			break;
		case 4 :
			printf("请选择您要的操作数:");
			scanf("%d %d", &a, &b);
			int h = mul(a, b);
			printf("%d\n", h);
			break;
		default :
			printf("您选错了请重新选择:");
			break;
		}
	} while (i);
	return 0;
}

2.第一次优化后的代码(回调函数)

void menu()
{
	printf("******************************\n");
	printf("***********简易计算器*********\n");
	printf("*******1.加法*****************\n");
	printf("*******2.减法*****************\n");
	printf("*******3.除法*****************\n");
	printf("*******4.乘法*****************\n");
	printf("*******0.退出*****************\n");
}
int Add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int div(int x, int y)
{
	return x / y;
}
int mul(int x, int y)
{
	return x * y;
}
void oay(int(*p)(int x, int y))        
{
	int a = 0;
	int b = 0;
	printf("请选择您想要运算的两个数:");
	scanf("%d %d",&a ,&b );
	int len = (*p)(a, b);
	printf("%d\n", len);
}
int main()
{
	menu();
	int i = 0;
	do
	{
		printf("请选择您想要的算法:");
		scanf("%d", &i);
		switch (i)
		{
		case 1:
			oay(Add);       
			break;
		case 2:
			oay(sub);     
			break;
		case 3:
			oay(div);
			break;
		case 4:
			oay(mul);
			break;
		case 0:
			printf("期待您的下次使用");
			break;
		default:
			printf("您填错数了,请重新填写");
			break;
		}
	} while (i);
	return 0;
}

3.转移表:

void menu()
{
	printf("******************************\n");
	printf("***********简易计算器*********\n");
	printf("*******1.加法*****************\n");
	printf("*******2.减法*****************\n");
	printf("*******3.除法*****************\n");
	printf("*******4.乘法*****************\n");
	printf("*******0.退出*****************\n");
}
int Add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int div(int x, int y)
{
	return x / y;
}
int mul(int x, int y)
{
	return x * y;
}
int main()
{
	menu();
	int i = 0;
	int a = 0;
	int b = 0;
	int(*p[5]) (a,b) = {0,add,sub,div,mul};     
	do
	{
		printf("请选择您想要的算法:");
		scanf("%d", &i);
		if (i <= 4 && i >= 1)
		{
			printf("请输入您想要的数字:");
			scanf("%d %d", &a, &b);
			int len = (*p[i])(a, b);        7
			printf("%d\n", len);
		}
		else if (i == 0)
		{
			printf("期待您的下次游玩");
			break;
		}
		else
		{
			printf("您输错了请重新输入:");
		}
	} while (i);
}

总结:

  今天小编也是写完了指针这部分的内容,也是终于结束了这部分的书写,说实在越到后期我感觉我掌握的知识越不牢固,就比如上面的函数指针数组,我其实已经忘记这个怎么定义了,这个也是边看以前写的代码边写博客才回忆到的,这里就展现了温故而知新的重要性,所以读者朋友们平时一定要多回顾自己以前学的内容,无论是编程还是别的,不然很容易遗忘的,这里小编就不多废话了,预告一下,下一篇小编就要写小编在学习C语言的时候恩师讲过的一些笔试题,我忘记了不少,所以决定写博客回顾一下,如果文章有错误,请在评论区指出,小编一定会汲取错误,那么,我们下一篇见喽!(ps:下一篇可能晚点发布)

 

 

 

   

  

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

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

相关文章

电脑桌面图标大小怎么调整?多种方法图文教程【全】

随着数字化生活的深入&#xff0c;电脑桌面图标的大小调整成为了我们日常使用中经常需要面对的问题。无论是为了更清晰地查看文件内容&#xff0c;还是为了美化桌面布局&#xff0c;掌握调整图标大小的方法都显得尤为重要。电脑桌面图标大小怎么调整&#xff1f;本文将为您提供…

LVGL开发教程-按钮Button

系列文章目录 知不足而奋进 望远山而前行 目录 系列文章目录 文章目录 前言 1. 普通Button 2.可选中Button 3.按钮事件处理 总结 前言 在图形用户界面&#xff08;GUI&#xff09;开发中&#xff0c;按钮&#xff08;Button&#xff09;是用户与程序交互的重要组件之一…

面向龙芯LoongArch平台的AMD GPU补丁解决了一个“巨大平台错误“

本周一Linux内核社区发布了一组补丁&#xff0c;旨在让老旧的 AMD Radeon GFX7/GFX8 时代图形处理器在龙芯LoongArch平台上运行。这些在Loongson平台上处理老旧Radeon Hawaii~Polaris GPU的补丁指出了这些中国计算系统的一个"巨大的平台错误"。 AMDGPU 和 Radeon 内核…

揭秘与应对:一打开移动硬盘就提示格式化的深度解析

在日常的数据存储与交换中&#xff0c;移动硬盘因其便携性和大容量而备受青睐。然而&#xff0c;有时我们可能会遇到一种令人困扰的现象&#xff1a;当试图打开移动硬盘时&#xff0c;系统会弹出一个警告窗口&#xff0c;提示“磁盘未被格式化&#xff0c;是否现在格式化&#…

跨境电商打造高效运营:自养号测评系统的五大优势

在当前的跨境电商行业&#xff0c;测评作为提升产品排名和促进销售的关键策略&#xff0c;其重要性日益凸显。为了在竞争激烈的市场中获得优势&#xff0c;卖家需要运用自养号测评系统等工具&#xff0c;以实现更高效的运营和更佳的业绩。 自养号测评系统具备多方面的优势&…

Python酷库之旅-比翼双飞情侣库(15)

目录 一、xlrd库的由来 二、xlrd库优缺点 1、优点 1-1、支持多种Excel文件格式 1-2、高效性 1-3、开源性 1-4、简单易用 1-5、良好的兼容性 2、缺点 2-1、对.xlsx格式支持有限 2-2、功能相对单一 2-3、更新和维护频率低 2-4、依赖外部资源 三、xlrd库的版本说明 …

hugging face:大模型时代的github介绍

1. Hugging Face是什么&#xff1a; Hugging Face大模型时代的“github”&#xff0c;很多人有个这样的认知&#xff0c;但是我觉得不完全准确&#xff0c;他们相似的地方在于资源丰富&#xff0c;github有各种各样的软件代码和示例&#xff0c;但是它不是系统的&#xff0c;没…

数据库 |试卷1试卷2

1.数据库语言四大语句 4.四大类&#xff08;DDL、DML、DQL、DCL&#xff09;_中度ddl-CSDN博客 数据定义&#xff08;data defination language&#xff09; 查询、创建、删除、使用 #查询所有数据库 show databases;#查询当前数据库 select database();#创建数据库 create …

利用DeepFlow解决APISIX故障诊断中的方向偏差问题

概要&#xff1a;随着APISIX作为IT应用系统入口的普及&#xff0c;其故障定位能力的不足导致了在业务故障诊断中&#xff0c;APISIX常常成为首要的“嫌疑对象”。这不仅导致了“兴师动众”式的资源投入&#xff0c;还可能使诊断方向“背道而驰”&#xff0c;从而导致业务故障“…

【CT】LeetCode手撕—46. 全排列

目录 题目1- 思路2- 实现⭐46. 全排列——题解思路 3- ACM实现 题目 原题连接&#xff1a;46. 全排列 1- 思路 模式识别 模式1&#xff1a;不含重复数字的数组 nums ——> 任意顺序 可能的全排列 ——> 回溯模式2&#xff1a;全排列 ——> 排列问题&#xff0c;不同…

PLC通过Profibus协议转Modbus协议网关接LED大屏通讯

一、背景 Modbus协议和Profibus协议是两种常用于工业控制系统的通信协议&#xff0c;它们在自动化领域中起着重要的作用。Modbus是一种串行通信协议&#xff0c;被广泛应用于各种设备之间的通信&#xff0c;如传感器、执行器、PLC等。而Profibus则是一种现场总线通信协议&…

可以把 FolkMQ 内嵌到 SpringBoot3 项目里(可内嵌的消息中间件)

之前发了《把 FolkMQ 内嵌到 SpringBoot2 项目里&#xff08;比如 “诺依” 啊&#xff09;》。有人说都淘态了&#xff0c;有什么好内嵌的。。。所以再发个 SpringBoot3 FolkMQ 是一个 “纯血国产” 的消息中间件。支持内嵌、单机、集群、多重集群等多种部署方式。 内嵌版&am…

SysTools MailXaminer: 电子邮件取证调查中的链接分析和时间线分析

天津鸿萌科贸发展有限公司是 SysTools 系列软件的授权代理商。 SysTools MailXaminer 电子邮件取证软件提供全面强大的解决方案&#xff0c;通过简化的操作&#xff0c;从电子邮件客户端、网络邮箱服务器、磁盘镜像、Skype 通讯工具中解密并搜索证据。软件对调查工作的每一阶段…

volatile原理

volatile内存语义 volatile是java提供的一种轻量级的同步机制&#xff0c;在并发编程中&#xff0c;它也扮演着比较重要的角色。一方面volatile不会造成上下文切换的开销&#xff0c;另一方面它又不能像synchronized那样保证所有场景下线程安全&#xff0c;因此必须在合适的场…

滑动窗口练习1-长度最小的子数组

1.题目链接&#xff1a;209.长度最小的子数组 2.题目描述&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条…

【C++】#20,#21

#20类和对象 #include <iostream>using namespace std;class Box{public: //公有 double length; //ctrle复制本行 double width;double height;void getVolume(){ //方法带&#xff08;&#xff09; cout<<"盒子体积为&#xff1a;"<<le…

聊聊redis中的有序集合

写在文章开头 有序集合(sorted set)是redis中比较常见的数据库结构&#xff0c;它不仅支持O(logN)界别的有序的范围查询&#xff0c;同时也支持O(1)级别的单元素查询&#xff0c;基于此问题&#xff0c;本文就将从redis源码的角度分析一下有序集合的设计与实现。 Hi&#xff0…

黄仁勋最新对话:未来互联网流量将大幅减少,计算将更多即时生成

黄仁勋表示&#xff0c;未来随着设备上运行的小语言模型变得更加上下文化和生成化&#xff0c;互联网流量将大幅减少&#xff0c;计算将更多地即时生成&#xff0c;这将极大地节省能源&#xff0c;使计算模型发生根本性转变。 要点 1、黄仁勋强调生成式AI正以指数速度增长&…

C++ 65 之 模版的局限性

#include <iostream> #include <cstring> using namespace std;class Students05{ public:string m_name;int m_age;Students05(string name, int age){this->m_name name;this->m_name age;} };// 两个值进行对比的函数 template<typename T> bool …

APS计划排程系统如何打破装备使用约束

APS计划排程系统是离散制造型企业在计划控制方向的重要支撑&#xff0c;它提供的是交期预测、订单排产计划、物料采购计划、人力分配计划等等。近些几年来&#xff0c;多品种、小批量、多订单的生产模式&#xff0c;让企业的计划员应接不暇、疲累不堪&#xff0c;传统的人工经验…