天天看点

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

@toc

本篇文章的内容仍然为指针进阶的相关内容,继续上一篇文章的内容。【c语言进阶】——指针(一) (字符指针,数组指针,指针数组)

指针令人头秃!!!!!!!!!

我们创建函数的时候,就会在内存中开辟一块空间,既然占用了内存空间,那就有对应的内存空间地址。

函数指针,顾名思义就是指向函数的指针。

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
注意: &amp; 函数名 和 函数名均表示函数的地址! <code>数组名 != &amp;数组名&lt;br/&gt;函数名 == &amp;函数名</code>

</blockquote>

我们怎么把函数的地址存起来呢

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

注意 :函数值指针的类型需要和返回函数值类型相同

就像这样:
【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

思考:函数指针如何使用呢?

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

通过函数指针,我们可以找到函数,然后去调用这个函数。 函数指针是 &amp;

函数名,而我们函数调用的时候可以直接使用函数名,那么这里通过函数指针调用函数也可以这样写:

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
既然这个地方的 可以省略,那么我们在使用的时候 可以用多个,也可以不要, 号在这里就是一个摆设,这个地方放 是为了方便理解 + 学习指针。
【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

阅读两个有意思的(阴间)代码:

这两个代码均是书籍《c陷阱与缺陷》中提及的内容,推荐阅读这本书。

这两个代码怎么阅读和理解呢?

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
1.先将 void () ()理解清楚,这个是一个函数指针,指针指向的函数返回类型是void的。 2.再理解(void () ())0 我们之前的学习中学到过强制类型转换,需要将强制转换之后的类型用括号()括起来,这个地方就是将 0 强制类型转换成void() ()类型。 为什么要将0强制类型转换成void() ()类型呢? 原因:想要将0当做某个函数的地址 深入扩展:如果一个数字想要当作一个地址,直接使用这个数字肯定是不行的,而是要将这个数字转换成也给地址编号的类型。这也是代码1中为什么要将0强制类型转换的原因。 3.接着再看((void () ())0),对一个指针加, 就是对其进行解引用操作,对函数指针解引用就是找到这个函数 4.((void () ( ))0)( ) ,((void () ())0)找到函数后,对其使用(), 就是调用函数,所以((void () ())0)(); 是一个函数调用。 整体理解下来就是:将0强制类型转换成一个函数指针void( )( ),再通过对这个函数指针进行解引用操作,找到这个函数,对其进行调用!
1.signal是一个函数声明 2.<code>signal函数的参数有两个,第一个是int类型,第二个是函数指针</code>,该函数指针指向的函数的<code>参数是int类型</code>,<code>返回类型是void(void(*)(int))</code> 3.*signal的返回类型是一个函数指针 因为 void(signal xxx)**,该函数指针指向的函数的参数是int类型,返回类型是void(void( )(int) )

这种形式看起来就比较复杂和难以理解,我们可以用typedef类型重定义对其进行简化:

在之前的学习中,我们使用过 typedef 来定义过无符号整型 typedef unsigned int u_int ;

但是我们并没有学过指针类型如何进行类型重定义,比如说 void( )(int) ,如果我们要将其进行重定义,可以写成:

typedef void( )(int) pfun_t; 这种形式吗?

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

我们尝试将其放到编译器下,就会发现编译器报错,显然这种方式是行不通的!

思考:那么可以将这个类型重定义后的名称类似与定义函数指针一样放到( )里面吗?也就是typedef void(pfun_t)(int);
【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

写成这种形式后,编译器没有在报错或者警告,说明这种方式是对的,实际上正确的书写方式也正是这样!

完整应该是这样书写:

虽然将一行代码变成了两行,但是简化后的代码更便于阅读和理解。

<code>typedef 在进行类型重定义的时候,如果是函数指针类型,那么名称需要放到* 旁边,也就是说</code>

<code>不能写成这种形式:typedef void(*)(int) pfun_t;</code>

<code>&lt;br/&gt;正确的形式是:typedef void(*pfun_t)(int);</code>

深入扩展:当函数的返回类型是一个函数指针的时候,函数名需要放到函数返回类型-- - 函数指针内部,而不是直接放到返回类型— 函数指针后面。 void( signal(int, void( )(int)) )(int);

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

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢 ?

int ( *parr1[10] )( ); √

int* parr2[10] ( ); x

int (*)( ) parr3[10]; x

答案是:parr1 parr1先和[结合,说明parr1是数组。 数组的内容是什么呢 ? 是int(*)()类型的函数指针。
【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

练习:

定义一个函数; char my_strcpy (char dest, const char* src);

练习要求:

1.写一个函数指针pf,能够指向my——strcpy 2.写一个函数指针数组 pfarr,能够存放4个my_strcpy函数的地址

答案:

1.char (pf)(char, const char ) 2.char (pfarr[4])(char, const char )

转移表 —《c和指针》这本书提及

请看下面简易计算器的例子:

1.使用switch……case语句

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
这里我们用函数调用的方式实现两个数之间的加、减、乘、除操作,但是当我们想要退出程序的时候,选择0并不能之间退出游戏,还需要进行输入两个操作数,这种方式明显不符合我们的预期,对其稍微进行改造以下:

下面我们通过函数指针数组的方式来实现吧!

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
利用函数指针数组的方式,不仅可以简化我们的代码,也可以方面后面的扩展,比如说后面我们添加一个取模功能或一个异或功能,整个代码仅仅需要进行细微的调整即可满足我们的要求:
【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
这个地方我们仅仅调整了打印的菜单栏,函数指针数组初始化的值以及switch语句中加上case 5,case6。即可满足我们的要求,后续如果进行更多的功能扩展也可以按照这种方式,非常简单。

通过函数指针来实现

我们将这个重复的代码封装到一个函数中:

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

例如:

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
这种方法代码也比较简洁,扩展性也比较好。 这种方法利用了回调函数,后面会详解回调函数!
<code>指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针</code>

如何写出指向函数指针数组的指针呢?

方法一:

①首先我们应该要知道该指针指向的函数指针数组到底是什么?以上面的代码为例:对应的函数指针数组是:int(p3[4])(int, int) = { 0 }; 该函数指针数组的类型是:int()(int, int) //去掉变量名p3和数组[4]剩下的就是数组的类型 int()(int,int) ②用指向函数指针数组的指针变量名替换原来的数组变量名,也就是用(p4)替换p3得到的就是int((p4)[4])(int, int) = &amp;p3;//指向函数指针数组的指针

方法二:

①指向函数指针数组的指针本质上也是指针,所以我们首先写(p4),这个指针指向的一个数组,数组元素个数是4,所以右边要紧接着加上[4], 变成(p4)[4] ②此时要清楚知道数组的元素类型,以上面的代码为例:对应的函数指针数组是:int(p3[4])(int, int) = { 0 }; 该函数指针数组的类型是:int()(int, int) 将①中写好的内容(p4)[4],放到类型int()(int,int) 后面,得到: int((*p4)[4])(int, int)//指向函数指针数组的指针

扩展:如果我们要继续往下写一个指向函数指针数组的指针数组,可以这样写:

int((p4[3])[4])(int, int)//指向函数指针数组的指针数组

回调函数就是一个通过函数指针调用的函数。

理解:如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这个函数是回调函数。 特点:回调函数不是由该函数的实现方直接调用(其实也就是回调函数自身),而是在特定的事件或条件发生时由另外的一方调用的(另一个函数调用),用于对该事件或条件进行响应。

举例:比如上面计算器的方法二实现方式就是利用了回调函数的方法。

冒泡排序复习回顾: 整型数组排序。那我们将这样使 用函数: 冒泡排序的思想:两两相邻的元素进行比较,并且可能的话需要交换。
【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
一趟解决一个数字的排序问题, 第一趟最大值9出现到最右侧, 第二趟8到右侧第二位 10个数字,需要进行9躺冒泡排序 n个数字 n - 1躺 一趟冒泡排序内部: 第一趟:10个数字待排序,9对比较 第二趟:9个数字待排序,8对比较 第三趟:8个数字待排序,7对比较 …… 第九趟:2个数字待排序,1对比较
【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
上面我们写的冒泡排序只能排整数,如果遇到浮点型、结构体或者其他类型,那么上面的冒泡排序函数就不能使用了。

在c语言的库函数中有一个用于任意类型排序的函数 qsort

qsort-- - 使用快速排序的方法

qsort相关信息:

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
void 类型讲解: void是无,空的含义,void 表示的是无类型的指针。 void* 指针可以接收任意类型的地址(void 可以看作指针万能筒,可以接收任意类型) void 类型指针不能进行解引用操作的。 理解:我们知道指针的类型决定了指针解引用操作时访问字节的个数,如果指针类型是void *,无类型指针,那么就无法得知该指针解引用访问多少个字节数。 void* 类型指针不能进行++ / ±整数运算的操作。 理解:我们知道指针类型的意义除了决定指针解引用时访问字节的个数外,还决定了指针 + -整数运算的步长,如果指针类型是void * 无类型指针,那么就无法得知该指针 + -的步长到底是多少个字节。
【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

qsort函数中为了排序任意类型的数据,所以参数均给了void *

(1)下面我们来试着用qsort排序一下整型数组,熟悉qsort的使用:

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

(2)下面再用qsort排序一下浮点型型数组,继续熟悉qsort的使用方式:

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

(3)利用qsort排序结构体数据

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

(4)模拟实现qsort排序任意类型的排序函数-- - bubble_sort

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数
【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) 前言5、函数指针6、函数指针数组7、指向函数指针数组的指针8、回调函数

继续阅读