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

前言:

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


目录:

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);        7printf("%d\n", len);}else if (i == 0){printf("期待您的下次游玩");break;}else{printf("您输错了请重新输入:");}} while (i);
}

总结:

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

 

 

 

   

  

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

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

相关文章

服务器数据恢复—NTFS文件系统下双循环riad5数据恢复案例

服务器存储数据恢复环境&#xff1a; EMC CX4-480存储&#xff0c;该存储中有10块硬盘&#xff0c;其中有3块磁盘为掉线磁盘&#xff0c;另外7块磁盘组成一组RAID5磁盘阵列。运维人员在处理掉线磁盘时只添加新的硬盘做rebuild&#xff0c;并没有将掉线的硬盘拔掉&#xff0c;所…

ARCGIS 如何对河流等线条图形进行Smooth处理——具有多个断点高阶版

1.线转点折点&#xff08;注意&#xff01;很重要&#xff0c;不是线转点&#xff09; 2.点转线步骤 ## 3 线的融合 2.1 新建Filed 》短精度类型》利用选择工具的 线文件。全选同一条河流点&#xff0c;进入Tabel的选择界面。给同一条河赋值同一个值。 大功告成&#xff01;…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《计及多类型储能调频容量动态申报的电能量与调频市场联合出清方法研究》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

深入探究RTOS的任务调度

阅读引言&#xff1a; 此文将会从一个工程文件&#xff0c; 一步一步的分析RTOS的任务调度实现&#xff0c; 这里选用FreeRTOS分析&#xff0c; 别的也差不多的&#xff0c; 可能在细节上有少许不一样。 目录 1&#xff0c; 常见嵌入式实时操作系统 2&#xff0c; 任务调度的…

HDFS 面试题(一)

1. 简述什么是HDFS&#xff0c;以及HDFS作用 &#xff1f; HDFS&#xff0c;全称为Hadoop Distributed File System&#xff0c;即Hadoop分布式文件系统&#xff0c;是一个分布式文件系统&#xff0c;由Apache Hadoop项目的一部分。它被设计用来在廉价的硬件上运行&#xff0c…

练习题-18 计算两个积分

计算积分 I ∫ R e − t 4 d t . I\int_{\mathbb{R}} e^{-t^4} dt. I∫R​e−t4dt. 解&#xff1a;令 x t 4 xt^4 xt4. 则 I 2 ∫ 0 ∞ e − x ⋅ 1 4 ⋅ x − 3 / 4 d x 1 2 Γ ( 1 4 ) I 2\int_0^\infty e^{-x} \cdot \frac{1}{4}\cdot x^{-3/4} dx\frac{1}{2} \Gamma(…

SQLite Delete 语句

SQLite Delete 语句 SQLite 的 DELETE 语句用于从表中删除数据。它是 SQL 数据库管理中非常基础且重要的操作之一。在使用 DELETE 语句时&#xff0c;可以删除表中的特定行&#xff0c;也可以删除整个表的数据。本文将详细介绍 SQLite 中的 DELETE 语句&#xff0c;包括其语法…

【机器学习】第11章 神经网络与深度学习(重中之重)

一、概念 1.神经元模型 &#xff08;1&#xff09;神经网络的基本组成单位 &#xff08;2&#xff09;生物上&#xff0c;每个神经元通过树突接受来自其他被激活神经元的信息&#xff0c;通过轴突释放出来的化学递质改变当前神经元内的电位。当神经元内的电位累计到一个水平时…

Linux虚拟化技术概览:从KVM到Docker

Linux虚拟化技术是现代数据中心和云基础设施的核心组成部分&#xff0c;它允许在同一台物理服务器上运行多个独立的操作系统实例&#xff0c;从而提高资源利用率、灵活性和安全性。从KVM到Docker&#xff0c;Linux虚拟化经历了从传统虚拟机到轻量级容器的演进&#xff0c;下面是…

时间戳转时间格式小记——个位数月份 如何去掉补位0的情况

工作中遇到的问题小记一下 时间戳&#xff1a;1710172800 时间格式&#xff1a;2024-03-12 00:00:00 在无要求的情况下这种情况很OK 当我们需要时间格式为&#xff1a;2024-3-12 00:00:00 那常规情况就不适用 解决&#xff1a; import time sjc 1710172800 # Windows 环境…

Kubernetes Dashboard

Dashboard Dashboard 的项目网站&#xff0c;可以查看说明文档和基本的使用情况。 下载yaml wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.6.0/aio/deploy/recommended.yaml注意需要修改镜像&#xff0c;不然可能会拉去不下来镜像 cat recommended.yaml…

人工智能--自然语言处理NLP概述

欢迎来到 Papicatch的博客 目录 &#x1f349;引言 &#x1f348;基本概念 &#x1f348;核心技术 &#x1f348;常用模型和方法 &#x1f348;应用领域 &#x1f348;挑战和未来发展 &#x1f349;案例分析 &#x1f348;机器翻译中的BERT模型 &#x1f348;情感分析在…

【C++】编程核心概念——核心特征

C编程核心概念 在C中&#xff0c;封装、继承和多态是面向对象编程的三大核心概念&#xff08;特征&#xff09;。这些特性为程序员提供了一种创建和管理复杂系统的方法&#xff0c;同时保持了代码的模块化、可重用性和可扩展性。下面分别解释这三个概念的作用&#xff1a; 封装…

java kotlin 命令行解析

在 Java 中解析命令行字符串可以使用以下几种方法&#xff1a; 1. 使用 args 参数 当程序启动时&#xff0c;主方法&#xff08;main 方法&#xff09;会接收一个 String 数组作为参数&#xff0c;即 args。这些参数就是从命令行传递过来的。 示例代码&#xff1a; object …

Ansible——template模块

目录 基本参数 选项参数 高级参数 基本用法 示例&#xff1a; 示例一&#xff1a;Nginx 配置 1. 目录结构 2. 模板文件&#xff1a;nginx.conf.j2 3. Playbook 文件&#xff1a;deploy_nginx.yml 4. 执行 Playbook 示例二&#xff1a;MySQL 配置 1. 目录结构 2. …

LLM大语言模型(GPT)的分布式预训练与微调及部署

LLM大语言模型(GPT)的分布式预训练与微调及部署 实现方案&#xff1a;设计并实现了一个大规模语言模型&#xff08;GPT&#xff09;的分布式预训练&#xff0c;结合RAG(文档、MySQL)、Agent、LLM连网等技术在基座上微调&#xff0c;以提高模型在特定领域任务上的性能和效率。 …

函数(下) C语言

函数下 嵌套调用和链式访问1. 嵌套调用2. 链式访问 函数的声明和定义1. 单个文件2. 多个文件3. static 和 extern3.1 static 嵌套调用和链式访问 1. 嵌套调用 嵌套调用就是函数之间的互相调用&#xff0c;每个函数就像⼀个乐高零件&#xff0c;正是因为多个乐高的零件互相无缝…

Nginx缓存之代理缓存配置

Nginx 的缓存功能是集成在代理模块中的&#xff0c;当启用缓存功能时&#xff0c;Nginx 将请求返回的响应数据持久化在服务器磁盘中&#xff0c;响应数据缓存的相关元数据、有效期及缓存内容等信息将被存储在定义的共享内存中。当收到客户端请求时&#xff0c;Nginx 会在共享内…

十四、 组合数学

人们在生活中经常会遇到排列组合问题。比如说&#xff1a;在 5 5 5个礼物中选 2 2 2个&#xff0c;问有多少种选取方法&#xff1f; 组合数学就是研究一个集合内满足一定规则的排列问题。这类问题如下&#xff1a; 存在问题&#xff1a;即判断这些排列是否存在计数问题&#x…

Java中的封装、继承和多态的概念是什么?

封装 封装是面向对象编程的一个核心概念&#xff0c;它涉及到隐藏对象的属性和实现细节&#xff0c;只对外公开接口&#xff0c;控制在程序中属性的读和修改的访问级别。封装的目的是增强安全性和简化编程&#xff0c;使用者不必了解具体的实现细节&#xff0c;而只是要通过外…