分享

【C语言更新】指向函数的指针

 乐创客搬运工 2021-01-27

文/Edward

  定义一个指向函数的指针

前面我们说了,指针的本质其实就是用来存放地址的变量,将一个对象的地址赋值给指针的过程被称为指向。我们前面已经讲过了指针指向变量,指针指向数组,指针指向字符串,指针指向函数的返回值(指针函数)等,那么指针是否可以直接指向某个函数呢?答案显然是可以的,因为一个函数被编译器编译好之后,它也是会被存放在某一块内存上的。而这个函数名其实就是这个函数存放的首地址,平时在函数调用的时候也是跳到函数名所在的地址处去执行程序。那现在所需要做的就是去定义一个可以指向函数的指针。
指向函数的指针简称为“函数指针”,其定义方式为:
类型 (*指针变量名)(参数列表);
需要注意的是,这里定义的只是一个可以用来指向函数的指针而已,具体这个指针要去指向的函数在下面需要另行定义。还有一点需要注意,定义指向函数的指针时,参数列表里面的内容一定要与需要指向函数的参数列表保证一致。如,我们程序里面有一个函数:
int GetMax(int *Array);
此时需要定义一个指向函数的指针去指向它,那么定义这个指针时,后面括号里面的参数列表也应该为“int *Array”。
我们可以写成:
int (*p)(int *Array);
定义好了指向函数的指针之后,接下来我们就可以使用这个指针去指向函数了,前面我们所说,一个函数的函数名就是它被编译完成之后存放在内存上面的地址,因此指向时,直接将函数名赋值给这个指向函数的指针即可,如:
p = GetMax;
函数指针指向函数之后,就可以使用它了,使用的时候和使用普通函数一样,只不过将函数名改成指针名称即可,其他的返回值和形式参数都和使用普通函数一样,如:
p(InputArray);
接下来,我们来写个程序介绍一下函数指针的使用。写一个程序,对传入数组中的元素进行奇偶判断操作,即如果元素是偶数,就将其用0替代,如果是奇数,则用1代替。这个要求很容易就能编写一个函数出来实现它。如图1所示。
图1 普通函数实现数组的奇偶判断

接下来,我们将图1中的函数改写成指针函数来实现,改写的方法很简单,只要定义一个可以指向OvenOrParity函数的指针,调用时只要用指针替换掉函数名即可。如图2所示。
图2 用函数指针调用

以上就是指向函数的指针使用的全部内容,使用常规函数来对比一下就显得非常简单了。但是函数指针真正复杂的地方并不是它的用法,而是它的遗忘曲线。可以说绝大多数朋友在学习的时候肯定都能学会它的用法,然后学完就遗忘了,不了解它的重要作用。

  如何使用指向函数的指针
其实在平时使用中,这种指向函数的指针对我们写一些简单的程序来说是没有任何帮助的,反而会使程序变得复杂。但是如果跳出具体的程序实现层面,找到程序架构层面来说,使用指向函数的指针可以使得程序更加灵活,方便,更加方便维护。这就牵扯到软件分层思想。
假设现在一个程序要交给两个程序员来完成,一个做上层的程序应用,另一个做下层的驱动。由于目前我们的C语言都是在PC环境下使用的,条件所限实现不了上述的应用,我们就利用指向函数的指针来模拟一下上述的应用。现在我们要操作两个设备,一个设备是串口,一个设备是网口,不管是哪个设备,其驱动提供给应用程序就两个接口,一个接口是write,一个接口是read。上层应用在使用时,只需要指明是哪个设备(假设串口的设备号是1,网口的设备号是2),然后再调用write或者read,终端上就会打印出相应的信息。
要实现上面的程序,利用指向函数的指针就很方便了,我们只需定义一个write指针和read指针,然后根据应用传入的设备号,分别指向不同的函数即可。为了模拟程序的分层效果,我们利用多文件的形式来实现上述的程序。
首先,新建几个文件,main.c,driver.c,driver.h,usart.c,usart.h,ethernet.c,ethernet.h。如图3所示。
图3 新建文件

这几个文件中,main.c模拟上层应用程序,driver.c和driver.h模拟中间层的统一驱动,而usart.c,usart.h,ethernet.c和ethernet.h分别模拟具体的串口驱动和网口驱动。
对于usart.c和usart.h,ethernet.c和ethernet.h文件里面,只需要顾及自己所对应的硬件即可,无需考虑上层软件具体怎么调用,因此可以写出如图4的程序。
图4 具体设备驱动

接下来,我们来编写中间驱动层的程序driver.h和driver.c,对于中间驱动层程序,简单的办法就是使用switch和case的形式通过传入的设备ID选择不同的驱动去实现,这是一种方式。但是现在使用指向函数的指针来实现,因此只需要在driver.c中定义两个同样的函数write和read,在这两个函数中分别定义两个函数指针,根据传入的ID号,使之分别指向不同的设备读写程序。如图5所示。
图5 中间驱动层程序代码

最后,只需要在主函数中指明设备ID,然后调用读写函数即可,最后写上Makefile文件。如图6所示。
图6 main函数,Makefile文件和运行结果

函数指针除了上面的用法之外,还有一个更加方便的作用,这个用法就是函数的回调,关于回调函数的内容,我们将会在后面学习完结构体之后和结构体结合起来讲述。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多