C语言之指针进阶(3),函数指针


目录

前言:

一、函数指针变量的概念

二、函数指针变量的创建

三、函数指针变量的使用

四、两段特殊代码的理解

五、typedef

六、函数指针数组

总结:


前言:

        本文主要讲述C语言指针中的函数指针,包括函数指针变量的概念、创建及使用。两段特殊代码的理解,typedef关键字的使用,以及函数指针数组的使用,最后还有一个代码案例--计算器的实现。希望对大家有所帮助。


一、函数指针变量的概念

函数指针变量,是用于存储函数地址的指针变量

那么,函数有地址吗?函数名与地址有关吗?

简单测试一下:

我们发现:函数名和取地址函数名得到的地址是一样的

函数名本身可以被视为一个地址,指向函数代码的起始位置,这与&函数名一样,使用起来并无区别


二、函数指针变量的创建

既然函数名和&函数名得到了函数的地址,我们要存储这种地址,就需要创建函数指针变量

定义语法:函数返回类型(*变量名)(形参类型,形参类型)

如:

调试监视其值:

观察类型我们可以得知,函数指针变量的类型就是:去掉变量名,剩下的就是函数指针变量的类型

函数指针类型解析:

int  (*pf4) (int x, int y)

  • int:pf4指向的函数类型
  • pf4:函数指针变量名,*表明其为指针变量
  • (int x, int y):pf4指向函数的参数类型和个数的交代


三、函数指针变量的使用

通过函数指针调用指针指向的函数

使用方法1:

该方法就是通过解引用指针,找到函数地址,然后传入参数,接收返回值

使用方法2:

直接写函数指针变量的变量名,然后传入参数,这与直接写函数名传入参数是一样的,因为该指针变量值就等于函数名的地址

其实方法1中,*pf前面的*号本身无实际作用

我们无论写多少*号,结果是一样的,编译器没有对这个符号进行相关操作,故相当于摆设了,不过为了提高可读性,一般是写一个*号

注意:*pf必须用括号括起来,因为左边优先级高于*号,不加括号会被编译器认为是解引用函数的返回值,而这是一个非法访问


四、两段特殊代码的理解

1.( *(  void (*)()  ) 0 ) ()

请问这段代码是什么意思?

这其实是一次函数调用的代码,调用0地址处的函数

那么如何分析这段代码呢,我们画图来理解:

注意:该代码出自《C陷阱与缺陷》一书,情景是一位程序员需要写一个独立运行于某种微处理器上的C程序,当计算机启动时,硬件将调用0地址处的子例程(函数)。于是设计出了这段代码


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

请问这段代码是什么意思?

答案是:这是一个函数声明。我们画图分析

以上难以理解的可能就是第3步,按照以往的理解,理论上函数的返回类型不应该写在函数名前面吗,也就是下面这样

我们发现把 void(*)(int) 的返回类型写在函数名前面,编译器报错了

这其实是语法规定的,函数指针作为返回类型,函数名需写在类型的里面,也就是*号的旁边,上面那种才是正确的写法


五、typedef

我们发现上面那段代码是不是较难看懂,如果能让其与一般函数写法一样,也就是函数返回类型写在前面就好了,这里就有一个方法,使用typedef重命名

typedef:对类型名进行重命名

例1:重命名unsigned int

例2:重命名数组指针

注意:数组指针重命名,重命名的名字要放在括号里边

现在,就让我们把void ( *signal( int , void(*)(int) ) )(int)进行重命名:

注意:与数组指针一样,我们重命名的名字要放在类型里面,之后我们不管是以返回类型写在函数名前面,还是以函数参数类型写在参数里都可以,这样看代码是不是明朗了许多


六、函数指针数组

函数指针数组:存储函数指针的数组,数组的元素都是一个个类型相同的函数指针

定义语法:见下面第二张图

例如:

如上,两个相同类型的函数指针变量我们可以定义,可是当相同类型的函数多起来后,如果我们需要储存这些函数的地址,就需要数组来接收才方便了。

如:

注意:定义函数指针数组,数组放在数组类型里边

调用数组元素时,就正常使用下标+传入参数即可

如果觉得以上内容简单,我们可以实现一个简单案例

简单计算器的实现:

#include <stdio.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)
{return x / y;
}void menu()
{printf("***************************************\n");printf("**********  1.Add     2.Sub  **********\n");printf("**********  3.Mul     4.Div  **********\n");printf("**********      0.exit       **********\n");printf("***************************************\n");
}int main()
{int(*pf_arr[5])(int, int) = { 0,Add,Sub,Mul,Div };int input = 0;do{menu();printf("请选择:\n");scanf("%d", &input);if (input >= 1 && input <= 4){int a = 0;int b = 0;printf("请输入两个整数:\n");scanf("%d %d", &a, &b);printf("%d\n", pf_arr[input](a, b));}else if (input == 0){printf("程序退出\n");break;}else{printf("选择错误,请重新选择:\n");}} while (input);return 0;
}


总结:

        以上就是本文的全部内容了,希望对大家有所帮助,哪里有问题欢迎提出,感谢大家的支持

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

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

相关文章

aws msk加密方式和问控制连接方式

msk加密方式 msk提供了两种加密方式 静态加密传输中加密 创建集群时可以指定加密方式&#xff0c;参数如下 aws kafka create-cluster --cluster-name "ExampleClusterName" --broker-node-group-info file://brokernodegroupinfo.json --encryption-info file:/…

【基于springboot+vue的房屋租赁系统】

介绍 本系统是基于springbootvue的房屋租赁系统&#xff0c;数据库为mysql&#xff0c;可用于日常学习和毕设&#xff0c;系统分为管理员、房东、用户&#xff0c;部分截图如下所示&#xff1a; 部分界面截图 用户 管理员 联系我 微信&#xff1a;Zzllh_

Wpf 使用 Prism 实战开发Day24

自定义询问窗口 当需要关闭系统或进行删除数据或进行其他操作的时候&#xff0c;需要询问用户是否要执行对应的操作。那么就需要一个弹窗来给用户进行提示。 一.添加自定义询问窗口视图 (MsgView.xaml) 1.首先&#xff0c;添加一个自定义询问窗口视图 (MsgView.xaml) <Use…

qmt量化教程4----订阅全推数据

文章链接 qmt量化教程4----订阅全推数据 (qq.com) 上次写了订阅单股数据的教程 量化教程3---miniqmt当作第三方库设置&#xff0c;提供源代码 全推就主动推送&#xff0c;当行情有变化就会触发回调函数&#xff0c;推送实时数据&#xff0c;可以理解为数据驱动类型&#xff0…

使用 Flask 和 Celery 构建异步任务处理应用

文章目录 什么是 Flask&#xff1f;什么是 Celery&#xff1f;如何在 Flask 中使用 Celery&#xff1f;步骤 1&#xff1a;安装 Flask 和 Celery步骤 2&#xff1a;创建 Flask 应用程序步骤 3&#xff1a;运行 Celery Worker步骤 4&#xff1a;启动 Flask 应用程序 结论 在构建…

C# NetworkStream 流的详解与示例

文章目录 一、NetworkStream类的基本概念1.1 NetworkStream类概述1.2 NetworkStream类属性1.3 NetworkStream类方法 二、NetworkStream的连接方式三、NetworkStream的传输模式四、NetworkStream类示例服务器端代码&#xff1a;客户端代码&#xff1a; 五、总结 在C#中&#xff…

刷代码随想录有感(77):回溯算法——含有重复元素的全排列

题干&#xff1a; 代码&#xff1a; class Solution { public:vector<int> tmp;vector<vector<int>> res;void backtracking(vector<int> nums, vector<int> used){if(tmp.size() nums.size()){res.push_back(tmp);return;}sort(nums.begin(),…

iCloud 照片到 Android 指南:帮助您快速将照片从 iCloud 传输到安卓手机

​ 概括 iOS 和 Android 之间的传输是一个复杂的老问题。将 iCloud 照片传输到 Android 似乎是不可能的。放心。现在的高科技已经解决了这个问题。尽管 Apple 和 Android 不提供传输工具&#xff0c;但您仍然有其他有用的选项。这篇文章与您分享了 5 个技巧。因此&#xff0c;…

云部署最简单python web

最近在玩云主机&#xff0c;考虑将简单的web应用装上去&#xff0c;通过广域网访问一下&#xff0c;代码很简单&#xff0c;所以新手几乎不会碰到什么问题。 from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return Hello, World!app.route(/gree…

plsql 学习

过程化编程语言 赋值&#xff1a;&#xff1a; ||&#xff1a;连接符号 dbms_output.put_line() :输出的语句 var_name ACCOUNTLIBRARY.USERNAME%type; 变量名&#xff1b;某个表的数据类型&#xff1b;赋值给变量名 用下面的方法更好用 异常exception 循…

Linux网络编程:HTTP协议

前言&#xff1a; 我们知道OSI模型上层分为应用层、会话层和表示层&#xff0c;我们接下来要讲的是主流的应用层协议HTTP&#xff0c;为什么需要这个协议呢&#xff0c;因为在应用层由于操作系统的不同、开发人员使用的语言类型不同&#xff0c;当我们在传输结构化数据时&…

算法打卡 Day9(字符串KMP 算法)-实现 strStr+ 重复的子字符串

KMP 算法 KMP 算法解决的是字符串匹配的问题&#xff0c;其经典思想是&#xff1a;当出现的字符串不匹配时&#xff0c;可以记录一部分之前已经匹配的文本内容&#xff0c;利用这些信息避免从头再去做匹配。 前缀表 next 数组就是一个前缀表。前缀表是用来回退的&#xff0c…

【启明智显技术分享】SOM2D02-2GW核心板适配ALSA(适用Sigmastar ssd201/202D)

提示&#xff1a;作为Espressif&#xff08;乐鑫科技&#xff09;大中华区合作伙伴及sigmastar&#xff08;厦门星宸&#xff09;VAD合作伙伴&#xff0c;我们不仅用心整理了你在开发过程中可能会遇到的问题以及快速上手的简明教程供开发小伙伴参考。同时也用心整理了乐鑫及星宸…

TypeScript学习日志-第三十二天(infer关键字)

infer关键字 一、作用与使用 infer 的作用就是推导泛型参数&#xff0c;infer 声明只能出现在 extends 子语句中&#xff0c;使用如下&#xff1a; 可以看出 已经推导出类型是 User 了 二、协变 infer 的 协变会返回联合类型&#xff0c;如图&#xff1a; 三、逆变 infer…

【C++】详解AVL树——平衡二叉搜索树

个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 祝福语&#xff1a;愿你拥抱自由的风 目录 二叉搜索树 AVL树概述 平衡因子 旋转情况分类 左单旋 右单旋 左右双旋 右左双旋 AVL树节点设计 AVL树设计 详解单旋 左单旋 右单旋 详解双旋 左右双旋 平衡因子情况如…

默认路由实现两个网段互通实验

默认路由实现两个网段互通实验 **默认路由&#xff1a;**是一种特殊的静态路由&#xff0c;当路由表中与数据包目的地址没有匹配的表项时&#xff0c;数据包将根据默认路由条目进行转发。默认路由在某些时候是非常有效的&#xff0c;例如在末梢网络中&#xff0c;默认路由可以…

ant design pro 6.0搭建教程

一、搭建 环境&#xff1a; Node.js 18.16.1 ant design pro 6.0 注意&#xff1a;选择umi3时&#xff0c;使用node.js 18版本的会报错&#xff0c;可以实践一下&#xff0c;这里就不再进行实践了。 umi3需要版本是低于node.js 18的 node下载地址&#xff1a; https://nodejs.…

韭菜的自我总结

韭菜的自我总结 股市技术面量价关系左侧右侧右侧技术左侧技术洗盘 韭菜的自我修养虚拟货币的启示韭菜的买入时机韭菜的心理压力成为优秀玩家的关键 股市技术面 技术面分析可以作为买卖时机判定的工具&#xff0c;但是投资还是需要基本面的分析作为支撑。也就是基本面选股&…

【C++】C++的心脏:深入理解内存管理中的 new 和 delete

欢迎来到CILMY23的博客 &#x1f3c6;本篇主题为&#xff1a; C的心脏&#xff1a;深入理解内存管理中的 new 和 delete &#x1f3c6;个人主页&#xff1a;CILMY23-CSDN博客 &#x1f3c6;系列专栏&#xff1a;Python | C | C语言 | 数据结构与算法 | 贪心算法 | Linux &a…

【C++进阶】AVL树

0.前言 前面我们已经学习过二叉搜索树了&#xff0c;但如果我们是用二叉搜索树来封装map和set等关联式容器是有缺陷的&#xff0c;很可能会退化为单分支的情况&#xff0c;那样效率就极低了&#xff0c;那么有没有方法来弥补二叉搜索树的缺陷呢&#xff1f; 那么AVL树就出现了&…