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

打印 上一主题 下一主题

主题 948|帖子 948|积分 2844

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

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

什么是函数指针?
函数指针,本质上是指针,该指针的地址指向一个函数。函数的定义是存储在代码段(.text)的,因此每个函数都有着本身的入口地址,函数指针就是指向代码段中函数入口地址的指针。函数指针的声明如下:
  1. ret (*p)(args,...)
复制代码
此中,ret是返回值类型,*p是一个整体,代表的是指向该函数的指针,args是形参列表,此中p叫做函数指针变量,也就是说p可以指向差异的函数。
   这里讲一下常常与函数指针一起被提及的“指针函数”。指针函数本质上是一个函数,该函数的返回值是一个指针
  基本用法

  1. #include <stdio.h>
  2. int sum(int a, int b)
  3. {
  4.     return a + b;
  5. }
  6. int main(int argc, char const *argv[])
  7. {
  8.     int (*p)(int, int); // 函数指针的初始化
  9.     p = sum;
  10.     printf("求和结果为:%d\n", p(1, 2)); // 输出:求和结果为:3
  11. }
复制代码
在上面的代码中,我们是用int (*p)(int, int);来初始化一个函数指针,注意这里*p的括号一定要有,表现p本质上是一个指针;有人可能会对上面的p=sum有所疑问,sum明显是函数名,为什么能举行赋值操纵呢?
   在数组中,数组名可以代表该数组的首地址。与数组类似的,函数名也可以代表函数的首地址,这个首地址通常叫做函数的入口地址,因此函数名就是函数本身的函数指针。因此赋值操纵现实上是把sum函数的函数入口地址赋值给了p,赋值之后p就是sum,所以调用p(1, 2)现实上就是调用sum(1, 2)。
  为什么要用函数指针?

当项目比较大,代码变得复杂了以后,函数指针就表现出了其良好性。这里借用一篇文章的例子:指针精讲
   如果我们要实现数组的排序,我们知道,常用的数组排序方法有很多种,比如快排,插入排序,冒泡排序,选择排序等,如果不管内部实现,你会发现,除了函数名不一样之外,返回值,包罗函数入参都是相同的,这时候如果要调用差异的排序方法,就可以使用指针函数来实现,我们只必要修改函数指针初始化的地方,而不必要去修改每个调用的地方。
  项目中的应用

回调函数

回调函数是指通过函数指针将一个函数作为参数传递给另一个函数,当满意某种条件或特定事件发生时,这个函数就会被调用。回调函数并不是由实现方直接调用,而是在特定的事件或条件发生时由另外一方来调用的。
  1. #include <stdio.h>
  2. int sum(int a, int b)
  3. {
  4.     return a + b;
  5. }
  6. int sum2(int a, int b)
  7. {
  8.     return a + a + b + b;
  9. }
  10. // 这个callback函数即为回调函数,其中第三个参数为函数指针
  11. int callback(int a, int b, int (*p)(int, int))
  12. {
  13.     return p(a, b);
  14. }
  15. int main(int argc, char const *argv[])
  16. {
  17.     int (*p)(int, int);
  18.     p = sum;
  19.     printf("1求和结果为:%d\n", callback(1, 2, sum));// 如果想使用sum2函数来求和,直接将callback里的sum替换为sum2即可。
  20. }
复制代码
callback是一个函数,在该函数里调用了另一个函数sum。在以上程序中,回调函数callback不必要关心sum或者sum2是如何实现的,只必要调用即可。
   这里问个问题:STM32的停止服务函数是一种回调函数吗?
  我认为STM32的停止服务函数并不是一种回调函数。停止服务函数固然也是在特定条件(停止产生且未举行屏蔽)下才会执行,但停止服务函数并没有调用其他函数,且处理器是通过查找停止向量表找到停止服务函数的入口地址进而执行停止服务函数。
FreeRTOS使命创建函数

  1. xTaskCreate(Task1, "Task1", 512, NULL, 4, &Task1_Handle);
复制代码
这里Task1是一个使命函数。通过查看xTaskCreate函数声明,如下所示:
  1. BaseType_t xTaskCreate(  TaskFunction_t pxTaskCode,
  2.                          const char * const pcName,
  3.                          const uint16_t usStackDepth,
  4.                          void * const pvParameters,
  5.                          UBaseType_t uxPriority,
  6.                          TaskHandle_t * const pxCreatedTask )
复制代码
从中可以看出,第一个输入参数是TaskFunction_t类型的,找到TaskFunction_t的定义,如下:
  1. typedef void (*TaskFunction_t)( void * );
复制代码
这是一种函数指针类型定义。这里使用typedef关键字定义了一个新类型TaskFunction_t,这个类型是一个函数指针,指向的函数返回类型为void,输入参数为void*。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张国伟

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表