【C语言】指针进阶[中](函数指针、函数指针数组、指向函数指针数组的指针)

news2024/11/15 9:15:56

简单不先于复杂,而是在复杂之后。

89efcc89ac61428db4d5b6639b2bd948.jpeg

目录

1. 函数指针

1.1 函数指针的用途 

2. 函数指针数组 

3. 指向函数指针数组的指针 


 

1. 函数指针

 

 

这里的 * 可以省略:

因为函数指针本质上是一个指针,存储了一个函数的地址,因此可以通过函数指针调用对应的函数。

但如果要写 * 的话必须要加( ):

将函数指针 pf 放在括号内,即 (*pf),表示对函数指针进行解引用操作,得到的是指向函数的指针。

因为函数名本身就是指向函数的指针,因此在这里加上括号,也可以看作是将函数名转化为指向函数的指针。

这段代码定义了一个函数 calc,它的参数是一个函数指针,指向一个可以接受两个 int 类型参数并返回 int 类型结果的函数。

calc 函数内部,定义了两个 int 类型变量 ab,并将它们初始化为 3 5。然后,调用函数指针 pf,传入参数 2 3,将返回值赋值给 ret,最后打印出 ret 的值。

main 函数中,调用 calc 函数,将函数名 Add 作为参数传入。

由于函数名可以自动转换为指向函数的指针,因此在 calc 函数内部可以直接使用 pf 调用 Add 函数。

总的来说,这个代码演示了如何使用函数指针作为参数,将函数作为参数传递给另一个函数,并在另一个函数内部调用传递进来的函数。

这是一种常用的技术,叫做回调函数,在我的C语言专栏中 :指针进阶【下】会详细讲解。

下面看两个有趣的代码:

 先不要往下滑,自己思考几分钟,这个代码的意义和作用是什么。

int main()
{
	(*(void (*)())0)();

	return 0;
}

注: 推荐一本书《C陷阱和缺陷》

(这两个有趣的代码在书中都会提到)

 同样,先不要向下滑,思考几分钟,能否理解这段代码的意义。

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

	return 0;
}

 

1.1 函数指针的用途 

接下来看这段代码:

 

 

//函数指针的用途

//写一个计算器
//加法、减法、乘法、除法
void menu()
{
    printf("****************************\n");
    printf("*******1. add  2. sub*******\n");
    printf("*******3. mul  4. div*******\n");
    printf("*******0. exit**************\n");
    printf("****************************\n");
}

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 input = 0;
    int x = 0;
    int y = 0;
    int ret = 0;
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch(input)
        {
        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;
        case 0:
            printf("退出计算器\n");
            break;
        default:
            printf("选择错误\n");
            break;

        }
    } while (input);

    return 0;
}

 用函数指针解决后的代码如下:

 

void menu()
{
    printf("****************************\n");
    printf("*******1. add  2. sub*******\n");
    printf("*******3. mul  4. div*******\n");
    printf("*******0. exit**************\n");
    printf("****************************\n");
}

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 calc(int(*pf)(int, int))
{
    int x = 0;
    int y = 0;
    int ret = 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 1:
            calc(Add);
            break;
        case 2:
            calc(Sub);
            break;
        case 3:
            calc(Mul);
            break;
        case 4:
            calc(Div);
            break;
        case 0:
            printf("退出计算器\n");
            break;
        default:
            printf("选择错误\n");
            break;

        }
    } while (input);

    return 0;
}

2. 函数指针数组 

数组是一个存放相同类型数据的存储空间,我们已经学习了指针数组:

 

那要把函数的地址存到一个数组中,这个数组就叫函数指针数组。

 

 

 

#define _CRT_SECURE_NO_WARNINGS 1
#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(*pf)(int, int) = Add;//pf是函数指针
	int (*arr[4])(int, int) = { Add,Sub,Mul,Div };//arr就是函数指针的数组

	int i = 0;
	for (i = 0; i < 4; i++)
	{
		int ret = arr[i](4, 8);
		printf("%d\n", ret);
	}

	return 0;
}

函数指针的用处:转移表。

我们来实现一个简易的计算器:

 

void menu()
{
    printf("****************************\n");
    printf("*******1. add  2. sub*******\n");
    printf("*******3. mul  4. div*******\n");
    printf("*******0. exit**************\n");
    printf("****************************\n");
}

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 input = 0;
    int x = 0;
    int y = 0;
    int ret = 0;

    int(*pfarr[5])(int, int) = { 0,Add,Sub,Mul,Div };


    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        if (input == 0)
        {
            printf("退出计算器\n");
        }
        else if (input >= 1 && input <= 4)
        {
            printf("请输入两个操作数:>");
            scanf("%d %d", &x, &y);
            ret = pfarr[input](x, y);
            printf("%d\n", ret);
        }
        else
        {
            printf("选择错误\n");
        }    

    } while (input);

    return 0;
}

3. 指向函数指针数组的指针 

指向函数指针数组的指针是一个指针

指针指向一个数组,数组的元素都是函数指针

 

 

 

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(*pfarr[5])(int, int) = { 0,Add,Sub,Mul,Div };

    //指向【函数指针数组】的指针
    int(*(*ppfarr)[5])(int, int) = &pfarr;

    return 0;
}

下面是函数指针函数指针数组函数指针数组的指针的区分:

 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

void test(const char* str)
{
	printf("%s\n", str);
}
int main()
{
	//函数指针pfun
	void (*pfun)(const char*) = test;
	//函数指针的数组pfunArr
	void (*pfunArr[5])(const char* str);
	pfunArr[0] = test;
	//指向函数指针数组pfunArr的指针ppfunArr
	void (*(*ppfunArr)[5])(const char*) = &pfunArr;

	return 0;
}

 

 

 

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

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

相关文章

解锁虚拟人IP“悦小满”新玩法,怎么用动捕设备化身多重身份?

在数字时代&#xff0c;虚拟人IP成为文化产业与数字技术、数字平台相融合的产物。虚拟人IP是文化IP文化产业发展的新内核。 广东网络广播电视台顺应时代发展&#xff0c;推出了全球首个粤语虚拟偶像“悦小满”&#xff0c;具备着Z世代所喜爱的2.5次元的3D虚拟形象&#xff0c;…

React 企业级开发必备, 我来带你快速入门 !

目录 一、为什么我来安利你学习 React? 二、使用 React 的前置工作 三、简易 demo 学开发 3.1、jsx语法 3.2、函数式组件 demo 3.3、简单类组件定义 3.4、复杂类组件定义 3.4.1、state 写法一&#xff1a;复杂式 写法二&#xff1a;简便式 3.4.2、props 类型限制…

界面干净整洁的windows桌面清单软件有哪些

无论是工作还是日常生活&#xff0c;我们都有各种各样的任务需要完成。然而&#xff0c;如果不进行有效的规划和管理&#xff0c;很容易导致遗漏和拖延&#xff0c;影响工作效率。而一个界面干净整洁的Windows桌面清单&#xff0c;能够帮助我们更好地规划和安排待办事项&#x…

优雅编码:WebStorm引领你迈向开发巅峰

目录 引言WebStrom的优点WebStorm的缺点自用插件推荐总结&#xff1a; WebStorm 官网 引言 WebStorm 是一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它提供了许多功能和工具&#xff0c;帮助开发人员优雅编码。 因为我是一个前端开发工程师&#xff0c;…

力扣 -- 面试题 17.16. 按摩师

题目链接&#xff1a;面试题 17.16. 按摩师 - 力扣&#xff08;LeetCode&#xff09; 下面是用动态规划的思想解决这道题的过程&#xff0c;相信各位小伙伴都能看懂并且掌握这道经典的动规题目滴。 参考代码&#xff1a; class Solution { public:int massage(vector<int…

哪款恢复数据的软件好用,数据恢复成功率高吗

我们的日常工作中免不了出现误删或者因为系统崩溃而丢失重要的文件的情况&#xff0c;这些文件的丢失会对我们的工作生活造成或多或少的损失。其实我们可以利用一些专业的软件来恢复这些丢失的数据。 许多网友会有疑问&#xff0c;哪款恢复数据的软件好用&#xff0c;数据恢复成…

2021-04-29 VSC++:吻合日。

缘由https://bbs.csdn.net/topics/399168383 void 吻合日() {//缘由https://bbs.csdn.net/topics/399168383int n 0, n1 0, y 0, y1 0, h 0, h1 0,z 0, j 0, x 0, t 0;std::cin >> n >> y >> h;z 自适年一月一日周一值(n) h;y1 y; t 返回月的…

Devops之制品库平台实践-nexus

Devops之制品库平台实践-nexus 目录 本节实践 实战名称&#x1f498; 实践&#xff1a;配置Maven代理仓库(测试成功)-2022.6.7&#x1f498; 实践&#xff1a;创建Maven本地仓库&#xff08;依赖&#xff09;-2022.6.10(测试成功)&#x1f498; 实践&#xff1a;创建Raw本地仓…

Java模拟OPC Server服务器并创建节点代码实现

前言 模拟OPC Server服务器的方法除了使用KEPServerEX6软件以外&#xff0c;还可以使用java代码模拟启动一个opc server。下文详细讲解&#xff0c;如何使用java代码&#xff0c;实现模拟一个或者多个opc server服务器。 引入依赖 首先在Maven项目的pom.xml文件中引入所需的…

量水堰计的作用是什么有什么特点

水库大坝安全监测中&#xff0c;渗流监测是一个重要的组成部分。量水堰计作为一种测量流量的仪器&#xff0c;在渗流监测中起着至关重要的作用。其工作原理是&#xff0c;在进水口处设置量水堰&#xff0c;量水堰上安装有磁致伸缩液位传感器&#xff0c;可以实时记录通过量水堰…

【ARM】-IRQ 和 FIQ 异常中断处理程序的返回

文章目录 处理流程示例代码实现 处理流程 通常处理器执行完当前指令后&#xff0c;查询 IRQ 中断引脚及 FIQ 中断引脚&#xff0c;并且查看系统是否允许 IRQ 中断及 FIQ中断。 如果有中断引脚有效&#xff0c;并且系统允许该中断产生&#xff0c;处理器将产生 IRQ 异常中断或 …

自动化测试selenium篇(一)

一、自动化测试 1.1自动化测试介绍 自动化测试就相当于将人工测试手段进行转换&#xff0c;让代码去执行 1.2 自动化测试的分类 单元测试、接口测试、UI自动化测试 二、selenium介绍 2.1 selenium是什么 Selenium是web应用中基于UI的自动化测试框架 2.2 selenium的特点…

Redis的主从复制模式

主从复制就是Redis集群的策略 配从库不配主库&#xff1a;小弟可以选择谁是大哥&#xff0c;但大哥没有权利去选择小弟 读写分离&#xff1a;主机写&#xff0c;从机读 集群配置和搭建 1.将虚拟机中的CentOS(192.168.40.100)重新复制两份 2.将新复制的静态ip改一下,192.168.40.…

https 证书到期,手动更新

记录一下&#xff0c;因为 3 个月后还会用到的。。 1. 验证域名所有权&#xff08;在某个目录下放置指定文件验证&#xff09; http://172.245.xxx.xxx/.well-known/pki-validation/3C42D2093F4B0237224A95xxxxxxx.txt 2. 替换下载下来的证书文件 3. 检查&#x1f9d0; 喜欢或…

JMeter正则表达式提取器实践

目录 前言&#xff1a; apply to&#xff1a; 要检查的响应字段&#xff1a;样本数据源 引用名称&#xff1a; 正则表达式&#xff1a; 模板&#xff1a; 匹配数字&#xff1a; 缺省值&#xff1a; 前言&#xff1a; JMeter是一个流行的性能测试工具&#xff0c;它提供…

《Effective C++》 全书内容提炼总结

个人博客地址: https://cxx001.gitee.io 本文阅读说明 孔子云&#xff1a;“取乎其上&#xff0c;得乎其中&#xff1b;取乎其中&#xff0c;得乎其下&#xff1b;取乎其下&#xff0c;则无所得矣”。 对于读书求知而言&#xff0c;这句古训教我们去读好书&#xff0c;最好是…

【基础算法】穷举法

穷举法Exhaustive method是使用最广泛、设计最简单&#xff0c;同时最耗时的算法&#xff0c;也被称为暴力法、蛮力法Brute force method。 两数之和 给定一个整数数组array和一个目标值target&#xff0c;请在数组中找出和为目标值target的两个整数&#xff0c;并输出它们在数…

【PHP语言】医院安全(不良)事件报告系统源码

一、系统概述&#xff1a; 医院安全&#xff08;不良&#xff09;事件报告系统是一种用于医院管理和监管的工具&#xff0c;旨在帮助医疗机构识别、跟踪、分析和解决各种医疗安全事件&#xff0c;提高医疗质量和患者安全。 医院安全&#xff08;不良&#xff09;事件报告系统采…

Linux:nginx基础搭建(源码包)

安装基础环境 准备一个nginx源码包 yum -y install pcre-devel zlib-devel gcc-c useradd -M -s /sbin/nologin nginx tar xf nginx-1.12.0.tar.gz -C /usr/src/ cd /usr/src/nginx-1.12.0/ ./configure --prefix/usr/local/nginx --usernginx --groupnginx --with-http_st…

【unity】RenderFeature的应用(生成水平面的网格线)

【unity】RenderFeature的应用&#xff08;生成水平面的网格线&#xff09; 在URP里RenderFeature是用于后处理效果上的&#xff0c;也还可以实现一些特殊的效果&#xff0c;比如生成网格线。我们可以使用 CommandBuffer来创建地面网格&#xff0c;这样的话可以通过调整 Comman…