菜而不能答(1)——函数指针

本文章用于记录以前面试时,因为太菜,被面试官问了答不上来的问题:函数指针

面试官:通过简历可以看出你项目经验还是比较丰富的,你比较会的也是C语言,那我问问你,你写项目过程中有哪些地方用到了函数指针?
我:(糟糕了,这个问题第一次被问到,以前背过面经但是太久了也忘了)我……balabala……
最后答了两个方面,面试完一查,完了,全错……

定义

什么是函数指针?
函数指针,本质上是指针,该指针的地址指向一个函数。函数的定义是存储在代码段(.text)的,因此每个函数都有着自己的入口地址,函数指针就是指向代码段中函数入口地址的指针。函数指针的声明如下:

ret (*p)(args,...)

其中,ret是返回值类型,*p是一个整体,代表的是指向该函数的指针,args是形参列表,其中p叫做函数指针变量,也就是说p可以指向不同的函数。

这里讲一下经常与函数指针一起被提及的“指针函数”。指针函数本质上是一个函数,该函数的返回值是一个指针

基本用法

#include <stdio.h>

int sum(int a, int b)
{
    return a + b;
}

int main(int argc, char const *argv[])
{
    int (*p)(int, int); // 函数指针的初始化
    p = sum;
    printf("求和结果为:%d\n", p(1, 2)); // 输出:求和结果为:3
}

在上面的代码中,我们是用int (*p)(int, int);来初始化一个函数指针,注意这里*p的括号一定要有,表示p本质上是一个指针;有人可能会对上面的p=sum有所疑问,sum明明是函数名,为什么能进行赋值操作呢?

在数组中,数组名可以代表该数组的首地址。与数组类似的,函数名也可以代表函数的首地址,这个首地址通常叫做函数的入口地址,因此函数名就是函数本身的函数指针。因此赋值操作实际上是把sum函数的函数入口地址赋值给了p,赋值之后p就是sum,所以调用p(1, 2)实际上就是调用sum(1, 2)

为什么要用函数指针?

当项目比较大,代码变得复杂了以后,函数指针就体现出了其优越性。这里借用一篇文章的例子:指针精讲

如果我们要实现数组的排序,我们知道,常用的数组排序方法有很多种,比如快排,插入排序,冒泡排序,选择排序等,如果不管内部实现,你会发现,除了函数名不一样之外,返回值,包括函数入参都是相同的,这时候如果要调用不同的排序方法,就可以使用指针函数来实现,我们只需要修改函数指针初始化的地方,而不需要去修改每个调用的地方。

项目中的应用

回调函数

回调函数是指通过函数指针将一个函数作为参数传递给另一个函数,当满足某种条件或特定事件发生时,这个函数就会被调用。回调函数并不是由实现方直接调用,而是在特定的事件或条件发生时由另外一方来调用的。

#include <stdio.h>

int sum(int a, int b)
{
    return a + b;
}
int sum2(int a, int b)
{
    return a + a + b + b;
}

// 这个callback函数即为回调函数,其中第三个参数为函数指针
int callback(int a, int b, int (*p)(int, int))
{
    return p(a, b);
} 

int main(int argc, char const *argv[])
{
    int (*p)(int, int);
    p = sum;
    printf("1求和结果为:%d\n", callback(1, 2, sum));// 如果想使用sum2函数来求和,直接将callback里的sum替换为sum2即可。
}

callback是一个函数,在该函数里调用了另一个函数sum。在以上程序中,回调函数callback不需要关心sum或者sum2是如何实现的,只需要调用即可。

这里问个问题:STM32的中断服务函数是一种回调函数吗?

我认为STM32的中断服务函数并不是一种回调函数。中断服务函数虽然也是在特定条件(中断产生且未进行屏蔽)下才会执行,但中断服务函数并没有调用其他函数,且处理器是通过查找中断向量表找到中断服务函数的入口地址进而执行中断服务函数。

FreeRTOS任务创建函数
xTaskCreate(Task1, "Task1", 512, NULL, 4, &Task1_Handle);

这里Task1是一个任务函数。通过查看xTaskCreate函数声明,如下所示:

BaseType_t xTaskCreate(  TaskFunction_t pxTaskCode,
                         const char * const pcName,
                         const uint16_t usStackDepth,
                         void * const pvParameters,
                         UBaseType_t uxPriority,
                         TaskHandle_t * const pxCreatedTask )

从中可以看出,第一个输入参数是TaskFunction_t类型的,找到TaskFunction_t的定义,如下:

typedef void (*TaskFunction_t)( void * );

这是一种函数指针类型定义。这里使用typedef关键字定义了一个新类型TaskFunction_t,这个类型是一个函数指针,指向的函数返回类型为void,输入参数为void*

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值