当前位置: 首页 > news >正文

函数指针和指针函数

指针函数

        指针函数就是一个返回值为指针类型的函数,没有特别的形式。

int *function()
{}

函数指针

        本文主要讲解的内容是函数指针。指针本身是一个变量,是用来指向内存地址的。一个程序运行时,所有和运行相关的物件都是需要加载到内存中,这就决定了程序运行时的任何东西都可以用指针来指向它。函数是存放在内存代码区域内的,它们同样有地址,因此同样可以用指针来存取函数,把这种指向函数入口地址的指针称为函数指针。函数指针可以将函数作为参数传递、实现回调机制、创建函数表等高级功能。

        函数指针的声明

        函数指针在声明时需要指明返回值和函数的入口参数,标准的声明格式如下:

返回值类型 (*指针变量名)(参数类型列表);// 声明一个指向无参数、返回int的函数的指针
int (*func_ptr)(); // 声明一个指向两个int参数、返回float的函数指针
float (*calc_func)(int, int);//声明一个指向两个int参数,返回int *的函数指针
int *(*calc_func)(int, int);

        函数指针的使用

        函数指针在使用之前必须先声明,标明返回值和函数的入口参数类型,个数。

//声明一个指向返回值为int,入口参数为两个int的函数
int (*operation)(int,int);int add(int a,int b)
{return (a + b);
}operation = add;//为函数指针赋值// 等效调用方式
int result1 = operation(3, 5);     // 推荐写法
int result2 = (*operation)(3, 5);  // 传统写法

        这里可以看出,我们将add函数作为变量赋值给了operation这个函数指针,此时operation就指向了add函数,调用operation就相当于调用了add函数,调用方式有两种,直接使用operation比较方便,但是看不出来他是一个函数指针,使用(* operation)比较麻烦,但是可以看出来这是一个函数指针。实际使用中可以根据个人喜好使用。

        声明一个函数指针以后,就可以在程序内动态对其赋值,修改其实现的函数功能不必重新编辑代码,增加了使用的灵活性,在需要实现其他的函数功能时只需要将其指向其他函数即可。

        如果我们需要定义多个接口相同的函数指针,可以使用以下方式。

int (*operation1)(int,int);
int (*operation2)(int,int);
int (*operation3)(int,int);

        这种方式比较繁琐,效率低下且不易更改,更改时需要更改全部的定义。我们可以使用typedef进行更加标准化的定义。

#include <stdio.h>//此时opr就变为了一个类型别名,表示一种数据类型,也就是函数指针类型,可以用来声明函数指针变量
//用它定义的函数指针都是指向函数类型为返回值为int,接受两个int类型参数的函数
typedef int (*opr)(int,int);//声明了operation是一个opr类型的函数指针
opr operation;int add(int a,int b)
{return (a + b);
}int main()
{//之后就可以正常使用函数指针operationoperation = add;printf("%d\n",add(2,3));
}

        回调函数

        回调函数是一种通过函数指针调用的函数,函数指针是实现回调函数的核心机制。回调函数通常是将函数作为参数传递给其他函数,使得这些函数能够在特定事件或条件发生时调用回调函数。        

        以下述代码为例

        回调函数实现:

#include <stdio.h>
//声明回调接口规范
typedef int (*data_callback)(int data);
//处理方案A,与调用方完全解耦,调用方不用关心具体实现。
void process_a(int data)
{if(data > 10)printf("%d\n");
}
//处理方案B,可以独立修改,不影响其他方案。
void process_b(int data)
{if(data < 10)printf("%d\n");
}
//通过此函数可以调用不同的函数来实现具体功能
void handle_data(data_callback process,int data)
{process(data);
}int main()
{handle_data(process_a,5);//采用方案A的数据处理方式
}

        直接在函数中调用:

#include <stdio.h>
//处理方案1
void process_a(int data)
{if(data > 10)printf("%d\n");
}
//处理方案2
void process_b(int data)
{if(data < 10)printf("%d\n");
}
void handle_data(int data)
{process_a(data);
}
int main()
{handle_data(5);
}

        对比回调函数,直接调用这种方法也可以实现相应功能,但是如果需要更改方案,就需要重新编写handle_data,且容易影响原有方案的实现,风险性高,扩展性差,耦合性强,handle_data强依赖于process_a,且无法在程序运行中动态切换不同的实现方案;相反,回调函数的扩展性极强,增加新功能只需要添加新的process函数,且不依赖于某一process,只依赖特定接口,耦合性弱,各模块独立运行,适合通用处理框架。

        举例说明:

        直接调用时修改方案:需要修改handle_data中的内容为其他方案的函数。

#include <stdio.h>
void process_a(int data)
{if(data > 10)printf("%d\n");
}
void process_b(int data)
{if(data < 10)printf("%d\n");
}
void handle_data(int data)
{process_b(data);
}
int main()
{handle_data(5);
}

        回调函数修改方案:可以在程序运行时修改不同方案,只需要修改传入的函数指针即可,且方案之间相互独立,不用修改handle_data函数的内容。

#include <stdio.h>
//声明回调接口规范
typedef int (*data_callback)(int data);
//处理方案A,与调用方完全解耦,调用方不用关心具体实现。
void process_a(int data)
{if(data > 10)printf("%d\n");
}
//处理方案B,可以独立修改,不影响其他方案。
void process_b(int data)
{if(data < 10)printf("%d\n");
}
//通过此函数可以调用不同的函数来实现具体功能
void handle_data(data_callback process,int data)
{process(data);
}int main()
{handle_data(process_a,5);//采用方案A的数据处理方式handle_data(process_b,5);//采用方案B的数据处理方式
}

        总结回调函数的应用场景主要有以下几种:

        1、需要多态行为时:通过函数指针可以在运行时选择和调用不同的函数,增加程序的灵活性。比如可以选择不同的数据处理方案,在使用时传入不同的函数指针即可实现不同方案,且方案之间相互独立。

        2、中断响应:回调函数用于处理特定事件,如用户输入、定时器中断触发等。

        3、策略模式:运行时通过回调函数选择不同算法。

        4、插件和模块化设计:在插件和模块化设计中,回调函数用于实现扩展点和自定义行为。

        回调函数最大的特点在于调用方和被调用方的双向解耦:减少模块间的直接依赖关系,使系统各部分独立,模块间仅通过约定好的接口交互,不关心内部实现。这也是区别于普通函数调用的最大差异。

        回调函数的价值在于调用方和被调用方的双向解耦:在主入口程序中,把回调函数像参数一样传入函数。这样一来,只要我们改变传进函数的参数,就可以实现不同的功能,且不需要修改函数的实现,变的很灵活,这就是解耦。解耦减少模块间的直接依赖关系,使系统各部分独立,模块间仅通过约定好的接口进行交互,不关心内部实现。但是使用回调函数会产生间接的调用性能消耗,在性能绝对优先的情况下慎用。

 

http://www.dtcms.com/a/286535.html

相关文章:

  • 【非辐射复合】半导体中非辐射载流子复合的双能级机制
  • 储能BMS通信“卡壳”?CAN转EtherCAT让电池数据“跑起来”
  • animation-timing-function动画的速度曲线
  • 面试150 被围绕的区域
  • 数据结构——单调栈
  • PHP语法高级篇(三):过滤器与JSON
  • 计算机“十万个为什么”之跨域
  • STM32 RTOS 开发基础:从任务管理到同步机制的全面解析
  • 深入解析PyQt5信号与槽的高级玩法:解锁GUI开发新姿势
  • codesys【串口】
  • 搜索 #1 DFS讲解
  • HBase2.5.4单机模式与伪分布式的安装与配置(Ubuntu系统)
  • Python学习笔记4
  • ts学习2
  • 用AI生成了一个名叫Janitor AI导航网站
  • Android性能优化之UI渲染优化
  • 静态时序分析:门控时钟建立时间检查
  • 无人机悬停技术运行与难点分析
  • Linux 服务器中,Tab 键自动补全功能失效
  • 免费好用,闪电般快速的AI 3D模型生成器
  • 信息检索革命:Perplexica+cpolar打造你的专属智能搜索中枢
  • 写在 35 岁生日的时候
  • Web3+AI融合新纪元:Sollong用智能终端重塑协作计算未来
  • unity Physics.RaycastNonAlloc
  • 反序列化漏洞1-PHP序列化基础概念(0基础超详细)
  • 从 Spring Boot 2.x 到 Spring Boot 3.x:全面对比与快速上手指南
  • 高精度流体分配系统的设计与分析
  • 加速度计和气压计、激光互补滤波融合算法
  • 接口测试时如何上传文件(图片、安装包等)
  • 基于DeepSeek大模型实现Function Call功能