详解函数指针变量,函数指针数组及转移表【超详细建议收藏点赞】

目录

  • 1. 函数指针变量
    • 1.1 函数指针变量的创建
    • 1.2 函数指针变量的使用
    • 1.3 两段有趣的代码
  • 2. 函数指针数组
  • 3. 转移表

1. 函数指针变量

什么是函数指针变量呢?
前面我们已经了解了整型指针与函数指针,通过类比我们可以知道:

函数指针变量是用来存放函数的地址的,未来通过地址能够调用函数

1.1 函数指针变量的创建

那么函数是否有地址呢?并且通过类比数组,函数名与&函数名的值是否有区别呢?
我们来测试一下:

#include <stdio.h>int add(int x, int y)
{return x + y;
}int main()
{printf("add  = %p\n", add);printf("&add = %p\n", &add);return 0;
}

输出结果:

在这里插入图片描述

我们发现两者的值是一模一样的。
我们知道数组名一般表示首元素地址,&数组名表示整个数组的地址,两者的值虽然一样,但是意义是完全不同的。
而函数名和&函数名表示的都是函数的地址,两者的意义完全相同,没有区别

如果我们要将函数的地址存放起来,就必须要创建函数指针变量,函数指针变量的写法其实和数组指针变量的写法十分相似(如果想学习数组指针变量,请移步我的主页浏览《详解指针数组,数组指针及(二维)数组传参(2)》,里面有详细介绍)。

#include <stdio.h>int add(int x, int y)
{return x + y;
}int main()
{//pf是专门用来存放函数地址的,pf就是函数指针变量int(*pf)(int, int) = add;int(*pf)(int x, int y) = &add;}

注意:参数类型名字写上或省略都是可以的

函数指针变量类型解析:

函数指针类型解析:![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimgblog.csdnimg.cn%2Fdirect%2Fc2266bcab54f4093b68036ab04f38b10.png&pos_id=img-ki0qhcqo-1708066617545

1.2 函数指针变量的使用

通过函数指针调用指针指向的函数。
代码实现如下:

#include <stdio.h>int add(int x, int y)
{return x + y;
}int main()
{int(*pf)(int, int) = &add;printf("%d\n", (*pf)(3, 4));printf("%d\n", (*pf)(45, 4));return 0;
}

输出结果:

在这里插入图片描述

1.3 两段有趣的代码

接下来介绍两段有趣的代码,读者请坐好,不要头晕眼花哦~

代码1:

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

由内而外层层解释:

void ( * ) () ---- 函数指针类型
( void ( * ) () ) ---- 强制类型转换
( void ( * ) () )0 ---- 将0强制类型转换为void ( * ) () 的函数指针类型
这就意味着我们假设0 地址处放着无参,返回类型是void的函数
最终是调用0地址处存放的这个函数

代码2:

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

画图解释:
在这里插入图片描述

所以这个代码表示的是一个函数声明。
函数名是signal,参数一个是整型,一个是函数指针类型, 返回类型也是一个函数指针类型的函数声明。

2. 函数指针数组

我们知道数组是一个存放相同类型数据的储存空间。
那么函数指针数组是什么呢?我们可以类比指针数组:

int main()
{int* arr[10];//数组的每个元素是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;
}int main()
{int (*pf1)(int, int) = add;//pf1是函数指针变量int (*pfarr[4])(int, int) = { add,sub,mul,div };//pfarr是函数指针数组
}

注意:定义函数指针数组最好是在函数指针变量的基础上进行,这样不容易出错。并且如果我们对函数指针数组初始化了,pfarr后面的4可以省略(类比数组)

3. 转移表

函数指针数组的用途:转移表的实现

举例:计算机的一般实现:

#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 input = 0;int ret = 0;int x = 0;int y = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 0:printf("退出计算器!\n");break;case 1:printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = add(x, y);printf("%d\n", ret);break;case 2:printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = sub(x, y);printf("%d\n", ret);break;case 3:printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = mul(x, y);printf("%d\n", ret);break;case 4:printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = div(x, y);printf("%d\n", ret);break;default:printf("选择错误,请重新选择。\n");break;}} while (input);return 0;
}

上面的代码可以实现两个整数的加减乘除运算,但是如果我们还要增加功能(比如>>,<<,|,^,&),我们就需要不断地增加case语句,这样会让代码重复代码非常多,非常冗长,不美观。

所以这里我们可以使用函数指针数组来优化:

#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 input = 0;int ret = 0;int x = 0;int y = 0;//定义函数指针数组--转移表int (*pfarr[5])(int, int) = { 0,add,sub,mul,div };//                            0  1   2   3   4do{menu();printf("请选择:>");scanf("%d", &input);if (input >0 && input <= 4){printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = pfarr[input](x, y);printf("%d\n", ret);}else if(input==0){printf("退出计算器!\n");}else{printf("选择错误,请重新选择.\n");}} while (input);return 0;
}

当然,也可以不用转移表的方式。下面是对第一份代码的另一种优化方式:

#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");
}void calc(int (*pf)(int, int))//函数指针
{int ret = 0;int x = 0;int y = 0;printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}int main()
{int input = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 0:printf("退出计算器!\n");break;case 1:calc(add);break;case 2:calc(sub);break;case 3:calc(mul);break;case 4:calc(div);break;default:printf("选择错误,请重新选择:>");break;}} while (input);return 0;
}

把每个case语句的功能封装成一个函数,使用函数指针,这样也可以避免代码的重复,冗余。

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

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

相关文章

【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱7(附带项目源码)

效果演示 文章目录 效果演示系列目录前言新增简单的泛型单例消耗品源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第25篇中&#xff0c;我们将探索如何用unity制作一个3D背包、库存、…

Leetcode-54. 螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示例 2&#xff1a; 输入&#xff1a;matrix [[1,2,3,…

一个比SDXL更快的模型——Stable Cascade【必坑指北】

2024年的春节假期&#xff0c;AIGC界又发生了重大革命性事件。 OpenAI 发布了首款文生视频模型——Sora。简单来说就是&#xff0c;AI视频要变天了&#xff01;之前的SVD&#xff0c;还是Google的Lumiere最多就几十帧&#xff0c;大约十秒左右&#xff0c;但是Sora却是SOTA级别…

MySQL数据库基础(二):MySQL数据库介绍

文章目录 MySQL数据库介绍 一、MySQL介绍 二、MySQL的特点 三、MySQL版本 四、MySQL数据库下载与安装 1、下载 2、安装 五、添加环境变量&#xff08;Windows&#xff09; 六、检测环境变量是否配置成功 MySQL数据库介绍 一、MySQL介绍 MySQL是一个关系型数据库管理…

5种风格非常经典的免费wordpress主题

免费wordpress主题下载 高端大气上档次的wordpress主题&#xff0c;也可以是免费的&#xff0c;可以在线免费下载。 https://www.wpniu.com/themes/288.html wordpress免费主题 高端大气的wordpress免费主题&#xff0c;LOGO在顶部左侧&#xff0c;导航菜单在顶部右侧。 ht…

C语言—for循环(2)

24⬆(2)并不是进阶的意思&#xff0c;而是这是我新的一天写的发布1,计算n的阶乘 ? n! 1*2*3.....*n n值通过键盘输入 /*1,计算n的阶乘 ? ?n! 1*2*3.....*n ?n值通过键盘输入*/#include <stdio.h>int main(void) {int n;printf("请输入一个整数来计算其阶乘…

每日一题——LeetCode1436.旅行终点站

方法一 个人方法 两次遍历set 终点站不通往其他任何城市&#xff0c;那么终点站只会出现在[cityA,cityB]的第二位&#xff0c;利用set第一次遍历保存所有站点&#xff0c;第二次遍历去除所有在第一位出现的站点&#xff0c;剩下的站点就是不通往任何站点的终点站&#xff1a; …

倒模UV树脂胶制作舞台监听耳返入耳式耳机壳可行吗?

使用倒模UV树脂胶制作舞台监听耳返入耳式耳机壳是一种可行的方法&#xff0c;能够为专业或业余的音乐制作人、DJ和舞台表演者提供定制的、高品质的监听耳返体验。 以下是一些关键步骤和注意事项&#xff1a; 耳模制作&#xff1a;首先&#xff0c;为使用者制作一个精确的耳模…

中科星图——LANDSAT_8/02/T1/RAW的Landsat8_C2_RAW类数据集

数据名称&#xff1a; Landsat8_C2_RAW 数据来源&#xff1a; USGS 时空范围&#xff1a; 2020年1月-2023年3月 空间范围&#xff1a; 全国 数据简介&#xff1a; Landsat8_C2_RAW数据集是经过缩放和校准的辐射亮度产品&#xff0c;按照数据质量划分为T1和T2。数据质量…

使用汇编程序恢复C库、动态链接器

文章目录 写在前面背景原理动态链接器C库 汇编代码示例删除C库删除动态链接器 写在前面 上层语言的好处就是方便&#xff0c;但无法触摸规则的底层&#xff0c;所有的规则都是别人制定的 学习底层原理不仅可以让我们对高级语言的规则有更深的理解&#xff0c;而且可以从自己的…

HiveSQL——连续增长问题

注&#xff1a;参考文章&#xff1a; SQL连续增长问题--HQL面试题35_sql判断一个列是否连续增长-CSDN博客文章浏览阅读2.6k次&#xff0c;点赞6次&#xff0c;收藏30次。目录0 需求分析1 数据准备3 小结0 需求分析假设我们有一张订单表shop_order shop_id,order_id,order_time…

springboot集成elk实现日志采集可视化

一、安装ELK 安装ELK组件请参考我这篇博客&#xff1a;windows下安装ELK(踩坑记录)_windows上安装elk教程-CSDN博客 这里不再重复赘述。 二、编写logstash配置 ELK组件均安装好并成功启动&#xff0c;进入到logstash组件下的config文件夹&#xff0c;创建logstash.conf配置…

性能分析5部曲:瓶颈分析与问题定位,如何快速解决瓶颈?

一、引言 很多做性能测试的同学都问过我这样一个问题&#xff1a;鱼哥(Carl_奕然)&#xff0c;你说性能测试的重点是什么? 我的回答很简单&#xff1a;瓶颈分析与问题定位。 在性能项目的整个周期&#xff0c;不管是脚本设计&#xff0c;脚本编写还是脚本执行&#xff0c;都…

在cloudcompare中,已知三维坐标可视化坐标点

主要思路是&#xff1a;已知坐标&#xff0c;通过在坐标生成圆球来可视化坐标点。主要操作步骤如下 1.在cloudcompare中找到“创建基础模型” 2.创建球体 输入坐标和球体半径&#xff0c;x&#xff0c;y&#xff0c;z中输入已知坐标&#xff0c;这里以&#xff08;0,0,0&…

小游戏和GUI编程(6) | 基于 SFML 的井字棋

小游戏和GUI编程(6) | 基于 SFML 的井字棋 0. 简介 使用 SFML 实现井字棋(tic-tac-toe), 规划如下: 了解规则&#xff0c; 使用命令行实现(已经实现了)使用 SFML&#xff0c;提供极简的交互(预计 1 小时)制作 SVG 图像&#xff0c; 美化界面(预计 1 小时) 1. 基于命令行的实…

MySQL安装问题:由于找不到MSVCP120.dll,无法继续执行代码.重新安装程序可能会解决此问题。

出现的问题&#xff1a; 解决&#xff1a;由于没有安装微软常用运行库合集64位导致的问题 下载vcredist_x64 https://www.microsoft.com/zh-CN/download/details.aspx?id40784 下载完成后&#xff0c;点击运行解决问题。

《VulnHub》GoldenEye:1

title: 《VulnHub》GoldenEye&#xff1a;1 date: 2024-02-16 14:53:49 updated: 2024-02-16 15:08:49 categories: WriteUp&#xff1a;Cyber-Range excerpt: 主机发现、目标信息扫描、源码 js 文件泄露敏感信息、hydra 爆破邮件服务&#xff08;pop3&#xff09;、邮件泄露敏…

撑住!再好的命,也有坎坷的时候

再好的命&#xff0c;其实都有为难的时候&#xff0c;都有经历磨难的时候。要想真正强大起来&#xff0c;都要度过一段没人帮忙&#xff0c;所有事情都是自己一个人撑&#xff0c;所有情绪和思想&#xff0c;都只有自己知道的日子。但只要咬牙撑过去&#xff0c;一切就都不一样…

【JAVA-Day86】守护线程

守护线程 守护线程摘要引言1. 了解守护线程&#xff1a;它是什么&#xff1f;&#x1f47b;特点和用途示例代码 2. 为何我们需要守护线程&#xff1f;&#x1f47b;辅助性任务处理不阻止程序的正常运行重要的清理工作示例代码&#x1f4da; 3. 如何创建和管理守护线程&#xff…

使用Taro开发鸿蒙原生应用——快速上手,鸿蒙应用开发指南

导读 本指南为开发者提供了使用 Taro 框架开发鸿蒙原生应用的快速入门方法。Taro&#xff0c;作为一个多端统一开发框架&#xff0c;让开发者能够使用一套代码同时适配多个平台&#xff0c;包括鸿蒙系统。文章将详细介绍如何配置开发环境&#xff0c;以及如何利用 Taro 的特性…