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

embedded/2024/10/22 16:19:05/


目录

前言:

一、函数指针变量的概念

二、函数指针变量的创建

三、函数指针变量的使用

四、两段特殊代码的理解

五、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.ppmy.cn/embedded/42793.html

相关文章

从 0 实现一个文件搜索工具 (Java 项目)

背景 各文件系统下, 都有提供文件查找的功能, 但是一般而言搜索速度很慢 本项目仿照 everything 工具, 实现本地文件的快速搜索 实现功能 选择指定本地目录, 根据输入的信息, 进行搜索, 显示指定目录下的匹配文件信息文件夹包含中文时, 支持汉语拼音搜索 (全拼 / 首字母匹配…

SpringMVC接收请求参数的方式:

接收简单变量的请求参数 直接使用简单变量作为形参进行接收&#xff08;这里简单变量名称需要与接收的参数名称保持一致&#xff0c;否则需要加上RequestParam注解&#xff09;&#xff1a; 细节&#xff1a; 1&#xff1a;SpringMVC会针对常见类型&#xff08;八种基本类型及…

什么是vue

Vue.js&#xff08;通常简称为 Vue&#xff09;是一个用于构建用户界面的渐进式 JavaScript 框架。它由尤雨溪&#xff08;Evan You&#xff09;创建&#xff0c;并于2014年首次发布。Vue 设计的目的是能够灵活地适应开发者的需求&#xff0c;可以通过引入不同的插件和库来构建…

数据可视化

目录 数据可视化: 常见的数据可视化库: Echarts 使用五步曲 Echarts 相关配置 数据可视化: 应对现在数据可视化趋势,越来越多企业需要在很多场景(营销数据,生产数据,用户数据)下使用,可视化图表来展示体现数据,让数据更加直观,数据特点更加突出 主要目的:借助于…

【数据结构与算法篇】二叉树链式结构及实现

【数据结构与算法篇】二叉树链式结构及实现 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 4. 二叉树链式结构的实现 4.1 前置说明 4.2 二叉树的遍历 4.2.1 前序、中序以及…

【XR806开发板试用】SPI驱动数码管显示

准备工作 安装repo 创建repo安装目录。 mkdir ~/bin下载repo wget https://storage.googleapis.com/git-repo-downloads/repo -P ~/bin/改变执行权限 chmod ax ~/bin/repo设置环境变量&#xff0c;在~/.bashrc文件的最后输入 export PATH~/bin:$PATH和export REPO_URLhttps://…

RabbitMQ .NET

setup rabbitmq docker run --namerabbit -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_USERadmin -e RABBITMQ_DEFAULT_PASSxxx -d rabbitmq:management ip:15672 login nuget RabbitMQ.Client Send //1.1.实例化连接工厂 var factory new ConnectionFactory() …

d20(184-190)-勇敢开始Java,咖啡拯救人生

目录 网络通信 网络通信三要素&#xff08;IP地址&#xff0c;端口号&#xff0c;协议 IP地址 InetAddress 端口号 协议 传输层的两个通信协议 UDP通信 java.net.Datagramsocket类 客户端 服务端 UDP通信多收多发 客户端 服务端 TCP通信 java.net.Socket类 客…