C语言进阶指南(15)(函数指针的创建与使用)

*欢迎来到博主的专栏——C语言进阶指南
博主id

文章目录

    • 函数指针
    • 函数指针的应用——回调函数
    • 函数指针数组

函数指针

函数也有地址(函数在调用的时候会占用内存空间,所以函数是有地址的),因此我们也可以用一个指针指向函数
1
函数指针的声明

type (*funcpointer)(element type……)

type是指向的函数的返回值,*与指针进行结合,声明这个标识符是指针而不是函数,element type是指针指向的函数的原型参数

函数也有地址,因此我们也可以用一个指针指向函数,数组名是数组首元素的地址,类似的,函数名也代表着函数的地址,因此函数的地址可以用函数名或者&函数名来代表

int add(int x, int y)
{return x+y;
}
int main()
{int(*pf)(int, int)=add;return 0;
}

pf是一个指向函数add的函数指针。通过对指针进行解引用,可以对函数进行调用

(*pf)(10,20);

函数指针的应用——回调函数

如果函数指针的作用仅仅是为了调用某个函数的话就有点脱裤子放屁的感觉了,事实上函数指针可以指向任何一个符合类型的函数。比如上述的pf可以指向任何一个返回类型为int,函数参数为(int,int)的函数。

由于这个特性,函数指针常常用于回调函数当中。

回调函数是函数参数有函数指针类型,且在程序的执行过程会调用传递的函数指针,根据传递的函数指针的不同,实现不同的效果的函数

回调函数的功能比一般的函数更加强大,这是因为回调函数能够用函数指针来完成自定义部分的内容(相当于是回调函数将部分的功能交由程序员自己定义)

以库函数qsort为例,qsort是一个在<stdlib.h>的库函数,他的功能是实现任何数据类型的数据的快速排序。

博主在先前文章中的自定义的快速排序和冒泡排序的函数都有一个不足之处,那就是只能被设定为排序固定的一中数据类型的数据

void bubble_sort(int* arr, int sz);

(用于排序int类型的冒泡排序)

void quick_sort(int arr[], int low,int high);

(用于排序int类型的快速排序)
如果想要程序排序float类型的数据,就需要在函数原型参数上修改数据类型

void bubble_sort(float* arr, int sz);
void quick_sort(int arr[], int low,int high);

而库函数qsort能够排序任何的数据类型,这就是回调函数功能更加强大的原因,首先来看看qsort的函数原型

在这里插入图片描述
函数原型参数base,base是指向待排序的数据的起始地址的void*指针。

为什么使用void*呢?因为qsort被设计成可以接收任何数据类型的函数,所以待排序的数据可以是char类型,int类型,float类型,它们对应的指针类型是char*,int*,float*。如果设置char*作为base的类型,那么它无法对其他指针类型进行操作
其他指针类型也是同理,只有void*被允许接收任意类型的指针参数

num是size_t类型的数据,是待排序的数据的个数
wideth是size_t类型的数据,是单个待排序的数据的字节数
compare是一个函数指针,这个函数指针指向一个返回值为int类型,参数类型为(void*,void*)的函数。

qsort函数的原理如下,第一个参数上传需要排序的数据的首元素地址,第二个参数上传数据的个数,第三个参数上传单个数据的字节。第四个函数传递的是自定义函数的函数指针,也是qosrt当中最主要的部分。

以排序int类型的数据为例,我们要定义compare函数是能够比较int类型数据的函数,根据MSDN中关于qsort的使用指南所说。
在这里插入图片描述

compare的第一个元素大于第二个元素。返回>0的值。
第一个元素等于第二个元素,返回0
第一个元素小于第二个元素,返回<0的值。

再根据函数指针的类型必须符合qsort中对于compare的函数指针的类型的要求是int*(void*,void*)。可以得出自定义compare函数的函数原型是

int compare(void*elem1,void*elem2);

为了使conpare函数能够实现比较int类型的数据。需要将elem1和elem2的数据类型改成int*。

这是因为void*类型的指针虽然可以接收任何类型的指针,但是不能对void*类型的指针进行操作,比如解引用,指针的加减算术运算等,解决方法就是将void*类型的指针根据需要,强制类型转换成其他类型的指针,再对指针进行操作

那么比较int的campare函数可以写成

int int_compare(void* elem1, void* elem2)
{return *(int*)elem1 - *(int*)elem2;
}

这样就满足了第一个元素大与第二个元素返回>0的值的效果。

如果想要一个比较float类型的compare函数可以写成

int float_compare(void* elem1, void* elem2)
{if (*(float*)elem1 > *(float*)elem2)return 1;else if (*(float*)elem1 == *(float*)elem2)return 0;else return -1;
}

这里不直接用elem1-elem2作为函数返回值。是因为conpare被qsort指定为一个返回类型为int类型的函数,两个浮点数相减的结果被转换成int类型可能会导致数据丢失,造成比较结果出现误差

前面提到了,函数名本身可以作为函数指针使用,那么使用qsort函数的例子为

int int_compare(void* elem1, void* elem2)
{return *(int*)elem1 - *(int*)elem2;
}
int main()
{int arr[10] = { 22,55,33,77,44,11,99,88,66,10 };qsort(arr, sizeof(arr) / sizeof(arr[0]), //qsort调用sizeof(arr[0]), int_compare);//qosrt调用return 0;
}

运行发现,arr被排序成升序了。
回调函数qsort的原理如下:

(1)qsort函数中只定义了排序部分,并没有定义比较元素的部分,比较元素的函数需要使用者提供,qsort根据使用者定义的比较函数来进行比较元素大小(通过函数指针来调用,因此称为回调函数)
(2)qsort中需要传入元素的个数和大小,是因为qsort接收的指针是void的,而void的指针不能进行加减算术运算,需要使用者提示元素的大小和个数来使指针定位元素的位置。

函数指针数组

函数指针数组是一个数组,数组中的元素都是函数指针
函数指针数组的声明形式如下:

type *iden[](element type)

和指针数组的声明一致,先让标识符与[]结合形成数组,再声明数组元素的类型(type*(element type))

函数指针数组的主要作用是将一些类型一致的函数的指针集合在一起,可以让程序变得简洁。

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;
}

假设有这么4个函数,如果我们想要根据不同的输入来调用不同的函数的话,可以用switch语句。

int main()
{int x = 0, y = 0;int input = 0;while (1){printf("请选择:");scanf("%d", &input);if (input > 0 && input <= 4){printf("请输入计算的两个数:");scanf("%d%d", &x, &y);}switch (input){case 1:add(x, y);break;case 2:sub(x, y);break;case 3:mul(x, y);break;case 4:div(x, y);break;case 0:return 0;default:printf("输入错误,请重试\n");}}
}

如果创建一个函数指针数组

int (*pf[5])(int,int)={NULL,add,sub,div,mul};
int main()
{int x=0, y=0,input=0;int (*pf[5])(int,int) = {NULL,add,sub,mul,div};do{scanf("%d", &input);if (input > 0 && input <= 4){printf("请输入两个数");scanf("%d%d", &x, &y);pf[input](x, y);//使用函数指针数组来调用函数}else if (!input)return 0;elseprintf("输入错误,请重试\n");} while (input);
}

从这么多的语句。
在这里插入图片描述
变成了
在这里插入图片描述
如果需要调用更多的函数的话,函数指针数组的作用会更加明显。

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

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

相关文章

提升认知|为什么比尔盖茨在地上发现100美元也会捡?

哈喽呀&#xff0c;大家好&#xff0c;我是雷工&#xff01; 大概在高中时代&#xff0c;听到过这么一个段子&#xff0c;“说如果地上有100美元&#xff0c;比尔盖茨是不会去捡的&#xff0c;因为他弯腰去捡100美元浪费的时间足够其创造1000美元以上的价值。” 当时听完也觉得…

stm32 计数模式

计数模式 但是对于通用定时器而言&#xff0c;计数器的计数模式不止向上计数这一种。上文基本定时器中计数器的计数模式都是向上计数的模式。 向上计数模式&#xff1a;计数器从0开始&#xff0c;向上自增&#xff0c;计到和自动重装寄存器的目标值相等时&#xff0c;计数器清…

fastReID论文总结

fastReID论文总结 fastReIDReID所面临的挑战提出的背景概念&#xff1a;所谓ReID就是从视频中找出感兴趣的物体&#xff08;人脸、人体、车辆等&#xff09;应用场景&#xff1a;存在的问题&#xff1a;当前的很多ReID任务可复用性差&#xff0c;无法快速落地使用解决方式&…

【MySQL】常用内置函数:数值函数 / 字符串函数 / 日期函数 / 其他函数

文章目录 数值函数round()&#xff1a;四舍五入ceiling()&#xff1a;上限函数floor()&#xff1a;地板函数abs()&#xff1a;计算绝对值rand()&#xff1a;生成0-1的随机浮点数 字符串函数length()&#xff1a;获取字符串中的字符数upper() / lower()&#xff1a;将字符串转化…

认证授权常见方式

认证授权 认证 (Authentication) 和授权 (Authorization)的区别是什么&#xff1f; 说简单点就是&#xff1a; 认证 (Authentication)&#xff1a; 你是谁。授权 (Authorization)&#xff1a; 你有权限干什么。 稍微正式点&#xff08;啰嗦点&#xff09;的说法就是&#x…

k8s-daemonset、job、cronjob控制器 6

Daemonset控制器&#xff08;一个节点部署一个&#xff09; 、 创建Daemonset控制器 控制节点上不能进行部署&#xff0c;有污点 解决方式&#xff1a; 扩容节点&#xff0c;token值过期的解决方法&#xff1a; 回收pod job控制器 需要使用perl镜像&#xff0c;仓库没有&…

GoLang切片

一、切片基础 1、切片的定义 切片&#xff08;Slice&#xff09;是一个拥有相同类型元素的可变长度的序列它是基于数组类型做的一层封装它非常灵活&#xff0c;支持自动扩容切片是一个引用类型&#xff0c;它的内部结构包含地址、长度和容量声明切片类型的基本语法如下&#…

“SRP模型+”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI 指数的生态质量评价及拓展

近年来&#xff0c;国内外学者在生态系统的敏感性、适应能力和潜在影响等方面开展了大量的生态脆弱性研究&#xff0c;他们普遍将生态脆弱性概念与农牧交错带、喀斯特地区、黄土高原区、流域、城市等相结合&#xff0c;评价不同类型研究区的生态脆弱特征&#xff0c;其研究内容…

NCo3.1(08) - Nco3 服务器端编程

本篇博文不再重复ABAP调用外部服务器的基础&#xff0c;只介绍 NCo3 开发的过程和要点。需要了解相关知识点的小伙伴们自行参考&#xff1a; SAP接口编程 之JCo3.0系列(06) - Jco服务器端编程 PyRFC 服务器端编程要点 创建项目 新建一个 Console 项目&#xff0c;选择 .Net …

用BootLoader更新S32K144的固件

1、工具&#xff1a;MDK及S32K144的支持包 创芯科技的USB转CAN 及 驱动 Jlink烧写器及驱动 链接&#xff1a;https://pan.baidu.com/s/1jGRdGVEzrO86CpP5UQ2fYQ 提取码&#xff1a;nihd IAP固件升级上位机 2、BootLoader底层文件 a、用MDK打开BootLoader工程 b、更改配置…

网络安全 | 使用人工智能阻止网络攻击

全球范围内分布式拒绝服务 (DDoS) 网络攻击急剧增加&#xff0c;这种数字攻击可以通过大量的互联网流量压垮目标服务器&#xff0c;从而使网站瘫痪。这种攻击每年都会发生数百万起&#xff0c;而且数量和规模都在不断增加。大约三分之一的网站宕机是由于 DDoS 攻击所致。 计算…

RabbitMQ消息的应答

消息的应答机制 消费者完成一个任务可能需要一段时间&#xff0c;如果其中一个消费者处理一个长的任务并仅只完成了部分突然它挂掉了&#xff0c;会发生什么情况。RabbitMQ 一旦向消费者传递了一条消息&#xff0c;便立即将该消息标记为删除。在这种情况下&#xff0c;突然有个…