• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > 详解C语言编程中的函数指针以及函数回调

详解C语言编程中的函数指针以及函数回调

作者:鑫有灵犀 字体:[增加 减小] 来源:互联网 时间:2017-05-28

鑫有灵犀 通过本文主要向大家介绍了c语言指针详解,c语言指针详解视频,c语言指针用法详解,c语言数组与指针详解,c指针详解等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

函数指针:

就是存储函数地址的指针,就是指向函数的指针,就是指针存储的值是函数地址,我们可以通过指针可以调用函数。

我们先来定义一个简单的函数:

//定义这样一个函数
void easyFunc()
{
  printf("I'm a easy Function\n");
}
//声明一个函数
void easyFunc();
//调用函数
easyFunc();

//定义这样一个函数
void easyFunc()
{
  printf("I'm a easy Function\n");
}
//声明一个函数
void easyFunc();
//调用函数
easyFunc();

</div>

上面三个步骤就是我们在学习函数的时候必须要做的,只有通过以上三步我们才算定义了一个完整的函数。

如何定义一个函数指针呢?前面我们定义其他类型的指针的格式是 类型 * 指针名 = 一个地址,比如:

int *p = &a;//定义了一个存储整形地址的指针p
</div>

也就是说如果我们要定义什么类型的指针就得知道什么类型,那么函数的类型怎么确定呢?函数的类型就是函数的声明把函数名去掉即可,比如上面的函数的类型就是:

void ()
</div>

我们再来声明一个有参数和返回值的函数:

int add(int a, int b);
</div>

上面函数的类型依旧是把函数名去掉即可:

int (int a, int b)
</div>

既然我们知道了函数的类型那么函数指针的类型就是在后面加个 * 即可,是不是这样呢?

int (int a, int b) * //这个是绝对错误的
</div>

上面这么定义是错误的,绝对是错误的,很多初学者都这样去做,总觉得就应该这样,其实函数指针的类型的定义正好比较特殊,它是这样的:

int (*) (int a, int b);//这里的型号在中间,一定要用括号括起来

int (*) (int a, int b);//这里的型号在中间,一定要用括号括起来

</div>

我们定义函数指针只需在 * 后面加个指针名称即可,也就是下面这样:

int (*p)(int a, int b) = NULL;//初始化为 NULL

int (*p)(int a, int b) = NULL;//初始化为 NULL

</div>

如果我们要给 p 赋值的话,我们就应该定义一个返回值类型为 int ,两个参数为 int 的函数:

int add(int a, int b)
{
  return a + b;
}
p = add;//给函数指针赋值

int add(int a, int b)
{
  return a + b;
}
p = add;//给函数指针赋值

</div>

经过上面的赋值,我们就可以使用 p 来代表函数:

p(5, 6);//等价于 add(5, 6);
printf("%d\n", p(5, b));

p(5, 6);//等价于 add(5, 6);
printf("%d\n", p(5, b));

</div>

输出结果为:11

通过上面的指针函数来使用函数,一般不是函数的主要用法,我们使用函数指针主要是用来实现函数的回调,通过把函数作为参数来使用。

函数指针的值

函数指针跟普通指针一样,存的也是一个内存地址, 只是这个地址是一个函数的起始地址, 下面这个程序打印出一个函数指针的值(func1.c):

#include <stdio.h>

typedef int (*Func)(int);

int Double(int a)
{
  return (a + a);
}

int main()
{
  Func p = Double;
  printf("%p\n", p);
  return 0;
}

</div>

编译、运行程序:

[lqy@localhost notlong]$ gcc -O2 -o func1 func1.c
[lqy@localhost notlong]$ ./func1
0x80483d0
[lqy@localhost notlong]$ 

</div>

然后我们用 nm 工具查看一下 Double 的地址, 看是不是正好是 0x80483d0:

[lqy@localhost notlong]$ nm func1 | sort
08048294 T _init
08048310 T _start
08048340 t __do_global_dtors_aux
080483a0 t frame_dummy
080483d0 T Double
080483e0 T main
...
</div>

  不出意料,Double 的起始地址果然是 0x080483d0。

函数回调

函数回调的本质就是让函数指针作为函数参数,函数调用时传入函数地址,也就是函数名即可。

我们什么时候使用回调函数呢?咱们先举个例子,比如现在小明现在作业有个题不会做,于是给小红打电话说:我现在作业有个题不会做,你能帮我做下吗?然后把答案告诉我?小红听到后觉得这个题也不是立刻能做出来的,所以跟小明说我做完之后告诉你。这个做完之后告诉小明就是函数的回调,如何告诉小明,小红必须有小明的联系方式,这个联系方式就是回调函数。接下来我们用代码来实现:

小明需要把联系方式留给小红,而且还得得到答案,因此需要个参数来保存答案:

void contactMethod(int answer)
{
  //把答案输出
  printf("答案为:%d\n", answer);
}

void contactMethod(int answer)
{
  //把答案输出
  printf("答案为:%d\n", answer);
}
小红这边得拿到小明的联系方式,需要用函数指针来存储这个方法:


void tellXiaoMing(int xiaoHongAnswer, void (*p)(int))
{
  p(xiaoHongAnswer);
}
//当小红把答案做出来的时候,小红把答案通过小明留下的联系方式传过去
tellXiaoMing(4, contactMethod);

void tellXiaoMing(int xiaoHongAnswer, void (*p)(int))
{
  p(xiaoHongAnswer);
}
//当小红把答案做出来的时候,小红把答案通过小明留下的联系方式传过去
tellXiaoMing(4, contactMethod);

</div>


上面的回调有人会问为什么我们不能直接 tellXiaoMing 方法中直接调用 contactMethod 函数呢?因为小红如果用函数指针作为参数的时候,不仅可以存储小明的联系方式,还可以存储小军的联系方式,这样的话我这边的代码就不用修改了,你只需要传入不同的参数就行了,因此这样的设计代码重用性很高,灵活性很大。

函数回调的整个过程就是上面这样,这里有个主要特点就是当我们使用回调的时候,一般用在一个方法需要等待操作的时候,比如上面的小红要等到答案做出来的时候才通知小明,不如当小明问小红时,小红直接能给出答案,就没必要有回调了,那执行顺序就是:
2016420152222457.png (458×514)
回调的顺序是:

2016420152256870.jpg (904×850)

上面的小红做题是个等待操作,比较耗时,小明也不能一直拿着电话等待,所以只有小红做出来之后,再把电话打回去才能告诉小明答案。

因此函数回调有两个主要特征:

函数指针作为参数,可以传入不同的函数,因此可以回调不同的函数
函数回调一般使用在需要等待或者耗时操作,或者得在一定时间或者事件触发后回调执行的情况下
我们使用函数回调来实现一个动态排序,我们现在个学生的结构体,里面包含了姓名,年龄,成绩,我们有个排序学生的方法,但是具体是按照姓名排?还是年龄排?还是成绩排?这个是不确定的,或者一会还会有新需求,因此通过动态排序写好之后,我们只需传入不同的函数即可。

定义学生结构体:

//定义个结构体 student,包含name,age 和 score
struct student {
  char name[255];
  int age;
  float score;
};
//typedef struct student 为 Student
typedef struct student Student;
</div>

定义比较结果的枚举:

//定义比较结果枚举
enum CompareResult {
  Student_Lager = 1, //1 代表大于
  Student_Same = 0,// 0 代表等于
  Student_Smaller = -1// -1 代表小于
};
//typedef enum CompareResult 为 StudentCompareResult
typedef enum CompareResult StudentCompareResult;
</div>

定义成绩,年龄和成绩比较函数:

/*
  通过成绩来比较学生
*/
StudentCompareResult compareByScore(Student st1, Student st2)
{
  if (st1.score > st2.score) {//如果前面学生成绩高于后面学生成绩,返回 1
    return Student_Lager;
  }
  else if (st1.score == st2.score) {//如果前面学生成绩等于后面学生成绩,返回 0
    return Student_Same;
  }
  else { //如果前面学生成绩低于后面学生成绩,返回 -1
    return Student_Smaller;
  }
}
 
/*
  通过年龄来比较学生
*/
StudentCompareResult compareByAge(Student st1, Student st2)
{
  if (st1.age > st2.age) {//如果前面学生年龄大于后面学生年龄



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C语言中条件编译详解
  • C语言中强制地址跳转详解
  • 详解C 语言项目中.h文件和.c文件的关系
  • C语言中 值传递和指针传递实例详解
  • 详解C语言中的函数、数组与指针
  • C语言 坐标移动详解及实例代码
  • 详解C语言中的字符串拼接(堆与栈)
  • C语言 经典题目螺旋矩阵 实例详解
  • C语言 文件操作解析详解及实例代码
  • C语言 冒泡排序算法详解及实例

相关文章

  • 2017-05-28vc中使用SendMessage自定义消息函数
  • 2017-05-28C语言 奇偶排序算法详解及实例代码
  • 2017-05-28探讨C++中不能声明为虚函数的有哪些函数
  • 2022-04-30分析第一个C语言程序
  • 2017-05-28Cocos2d-x保存用户游戏数据之XML文件是否存在问题判断方法
  • 2017-05-28C++函数pyrUp和pyrDown来实现图像金字塔功能
  • 2017-05-28C 语言指针变量的运算详解
  • 2017-05-28C++派生访问说明符小记(推荐)
  • 2017-05-28c++函数指针使用示例分享
  • 2017-05-28stl容器set,map,vector之erase用法与返回值详细解析

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • c语言字符数组与字符串的使用详解
    • 详解C语言中的常量指针和指针常量
    • C++直接初始化与复制初始化的区别深入解析
    • C++线程同步实例分析
    • C++条件及循环语句的综合运用实例
    • C++设置系统时间及系统时间网络更新的方法
    • C++编程中的数据类型和常量学习教程
    • VC创建圆角dialog的实现方法
    • 浅谈几种常见语言的命名空间(Namespace)
    • 浮点数在计算机中存储方式是怎样的

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有