前言:Hello!我是@每天都要敲代码。上一期我们一起学习了:字符指针的使用、指针数组和数组指针的理解、数组和指针的传参以及补充的一些练习题目<<传送门>>;还没有掌握的一定要去学习在看下面的知识点,因为指针的知识点都是连贯的!这一期我们将继续学指针剩余的内容,一起加油吧!
目录
1.3.2 代码2 void (*signal(int,void(*)(int)))(int)
1. 函数指针
1.1 函数指针的理解和写法
我们先根据前面所学梳理清楚函数指针是什么?
int* p 整型指针—指向整型的指针—存放的是整型变量的地址
char* pc 字符型指针—指向字符型的指针—存放的是字符变量的地址
int(*p)[10] 数组指针—指向数组的指针—存放的是数组的地址
———- 函数指针—肯定就是指向函数的指针—存放的是函数的地址
我们推导出了函数指针的意义,那么怎么书写呢?
这里我只说两点:
1、我们发现数组指针和函数指针的书写形式很类似:int (*parr)[10] = &arr
int (*pf)(int,int) = &ADD
2、我们打印&ADD和ADD的地址居然也是一样的,也类似于数组&arr和arr;我们来做一下比较:
(1)数组名 != &数组名;两者不等价,前者取出来的是首元素的地址;后者取出来的是整个数组的地址
(2)函数名 == &函数名;两者是等价的,因为对于函数没有什么首元素的地址这一说注意:()的优先级也要高于*号的,所以必须加上()来保证pf先和*结合
练习:有了上面的理解,我们不妨就拿一个例题来来练练手,看你是否真正理解掌握了函数指针的写法,例如:void test(char* str),函数指针怎么书写呢?void (*pt)(char*) = &test
1.2 函数指针的调用
我们还是先从已学的知识入手,然后剖析函数指针的调用
这里解释一下两点:
1、int (*pf)(int, int) = &ADD 等价于 int (*pf)(int, int) = ADD
2、int sum = (*pf)(2, 3) 等价于 int sum = pf (2, 3);这里的解应用*没有实际意思,是摆设
1.3 两段有趣的代码
1.3.1 代码1 (*(void (*)())0)()
( *( void (*)() )0 )() 我们先根据自己的想法通俗理解一下:将0强制类型转换为函数指针,然后解应用,在调用。
解析:
1、从数字0开始着手,想让数字0作为地址,肯定要是一种指针类型才可以;
2、把0强制类型转换为函数指针类型,0作为函数的地址;
3、0放到函数指针类型里是无参的,返回类型是void;
4、然后开始调用,我们解应用括号括起来,()代表我们调用它,参数什么都不传;
总结:调用0地址处的函数;该函数无参,返回类型是void
1、void(*)()—函数指针类型
2、(void (*)())0—对0进行强制类型转换,被解释为一个函数地址
3、*(void (*)())0—对0地址进行解引用操作
4、(*(void (*)())0)()—调用0地址处的函数
1.3.2 代码2 void (*signal(int,void(*)(int)))(int)
void (* signal(int,void(*)(int)) )(int)我们还是要一步步拆分去理解
1、signal和()先结合,说明signal是函数名;
2、signal函数的第一个参数的类型是int第,二个参数的类型是函数指针;该函数指针,指向一个参数为int,返回类型为void的函数;
3、signal函数的返回类型也是一个函数指针;该函数指针,指向一个参数为int,返回类型为void的函数;所以,signal是一个函数的声明;
4、void (*)(int) signal(int,void(*)(int))我们也可以这样理解,signal函数的返回类型是一个函数指针;但是语法上不能这样写,只能把返回类型写中间;简化:signal是一个参数函数指针,它的返回类型又是一个函数指针;那我们怎么优化呢?
利用typedef-对类型重定义:typedef void(*) (int) pfun_t;对void(*)(int)的函数指针类型进行重定义为pfun_t;但是这种语法是不支持的,我们要把pfun_t写到中间:
typedef void(*pfun_t) (int);重名之后上面代码就可以拆分成:
void (*)(int) signal(int,void(*)(int)) 和typedef void(*pfun_t) (int)等价于pun_t signal(int,punt)
是不是更加的容易理解了?
2. 函数指针数组
2.1 函数指针的理解和写法
我们先给出对于函数指针数组的理解:存放函数指针的数组;
我们在一起回顾一下整型指针数组:整型指针 int*,整型指针数组 int* arr[10];那么函数指针数组怎么定义和使用呢?我们通过一段代码的形式去理解。
2.2 函数指针的实际应用
那么函数指针在实际写代码中有什么应用呢?我们就通过一个典型的计算器来对比学习;比如:我们要实现加、减、乘、除。
2.2.1 普通方法实现计算器
我们从switch中就可以看出,代码很冗余。那我们思考一下:
1、如果把 printf(“请输入两个操作数:>”);scanf_s(“%d %d”, &x, &y);这两句代码全部提炼出去放到switch最前。printf(“ret=%d\n”, ret);这句代码提炼出区放到后面不久可以避免代码冗余了?
2、但是这样会出现我们问题,当我们选择5错误或者选择0退出时,它还是要让我们输入两个操作数才能退出,这就很怪异;包括打印也是,我们只有正确调用了函数,才能打印;所以这种方法并不可取;怎么办呢?下面我们就用函数指针数组来优化这个代码!
2.2.2 函数指针数组实现计算器
我们利用函数指针数组来实现计算器,即解决了代码冗余的问题,而且还让代码更加的简练了,是不是很妙?这里函数指针数组parr相当于一个跳板的作用,我们经常把这样的数组叫做转移表!
函数指针数组的用途:转移表
3. 指向函数指针数组的指针(了解)
3.1 指向函数指针数组的指针的理解和写法
指向函数指针数组的指针:是一个指针,指针指向一个数组 ,数组的元素都是函数指针 。
函数指针数组
取出函数指针数组的地址
整型数组:
int arr[5];
int (*p1)[5] = &arr
整型指针数组:
int* arr[5]
int* (*p2)[5] = &arr;p2是指向【整型指针数组】的指针
函数指针数组
int (*p)(int, int);函数指针
int (*p2[4])(int, int);函数指针的数组
int* (* (*p3)[4])(int, int) = &p2;取出的是函数指针数组的地址
p3就是一个指向【函数指针的数组】的指针
这部分很难,也比较绕,我们根据下面这个例题进行了解就好:
补充:区分数组元素类型和数组的类型
例如:int arr[10];数组元素类型—int,arr数组类型是—int [10]
4. 回调函数
4.1 回调函数的定义和理解
回调函数:就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
理解: A函数—–》B函数(A函数的地址、指针);A函数不是直接调用,把A函数的地址传递给B函数;B函数通过A函数的指针反过来调用A函数。要借用参数为函数指针!
4.1.1 利用回调函数来实现计算器
我们前面已经有两种方法了实现计算器,现在我们继续用回调函数来优化计算器,简直妙不可言!
4.2 利用回调函数模拟实现qsort函数
在利用回调函数模拟实现qsort函数之前,我们先回顾一下以前学过的冒泡排序和qsort快速排序,对比一下两者的区别!
4.2.1 回顾冒泡排序
上面我们是按照升序进行排序;冒泡排序很好理解,所以代码也很容易实现;但是有一个弊端:只能排整形数据;所以这就限制了它的使用!
4.2.2 回顾快排qsort
对于库里面的qsort函数,它就比较通用;什么样的数据都能进行排序 !
1、排整形数据
2、排结构体数据
4.2.3 qsort函数的模拟
我们在模拟实现前,不妨先看一下qsort函数的参数:
void qsort(void* base, base存放的是待排序数据中第一个对象的地址
size_t num, 排序数据元素的个数
size_t width, 排序数据中一个元素的大小,单位是字节
int(* compare)(const void* e1, const void* e2) 函数指针—是用来比较待排序数据中的2个元素的函数。对于qsort函数的模拟:我们可以在qsort使用的基础上进行修改,原来qsort是内部函数,拿来就能用,现在我们改成my_qsort去模拟实现my_qsort这个函数就可以!
最终:e1-e2就是升序排,e2-e1就是降序排!
代码的解析都已经写到注释里,如果还不明白的小伙伴,不如自己去调试运行一下,看看他们运行的逻辑。另外我们实际上知识利用my_qsort模拟实现了qsort函数,其它使用的方式还是和qsort函数一样的。对于排结构体数据也是没问题的,这就留给读者自己去尝试一下;相信你肯定有所收获!
总结:
这一章节,我们把指针剩余的部分就讲完了。主要讲解了:函数指针、函数指针数组的使用;指向函数指针数组的指针的了解;以及利用普通法、函数指针数组法、回调函数法实现计算器;回调函数的理解;利用回调函数模拟实现qsort;内容虽然不多,但是满满的都是干活!希望大家可以慢慢吸收;下一篇我们就开始补充关于指针的例题,相信会让你很有收获!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/128529.html