文档章节

C/C++回调函数简要介绍

jungleliu0923
 jungleliu0923
发布于 2014/02/10 10:53
字数 977
阅读 7211
收藏 143

1、引子

在C/C++里面有个非常给力的库函数qsort,相信大家都用过。他的函数原型如下:

void qsort(void *base,size_tnmemb,size_tsize,  int(*compar)(constvoid*, constvoid*));

使用的时候需要传递需要排序的数组base, 数组数目nmeb, 每个数组大小size,以及我们比较自定义的回调函数:compar.

2、概念

如compar所示,如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

和回调函数相对,普通函数调用一般为同步调用,即A模块阻塞调用B模块函数,B模块执行完毕之后会讲结果返回给A模块。

回调函数则不同。A模块定义一个回调函数C,将函数指针C传给B模块作为参数。在调用B模块之后,B模块会根据一定的条件触发回调C,进而重新调用模块A的函数。

这样模块B就不用关心回调函数自己的具体实现,这样可以是系统架构更加清晰,可以提高系统的可扩展性。

3、举例

现在我们有个模块叫callback模块。callback模块会接受主函数main传来的a,b,回调函数c.

如果a==100,那么就返回10000,否则就使用我们的回调函数,执行c(a,b),并返回。

具体如下所示:

3.1、通用头文件 pub.h

这里主要定义了一个函数指针。

函数原型为 int myfoo(int, int); 

如果有疑问,请自行Google or Baidu.

/***************************************************************************
 *
 * Copyright (c) 2014 liujun&&baidu. inc All Rights Reserved
 *
 **************************************************************************/

/*
 * @filename:  pub.h
 * @version:  1.0
 * @date : 2014/01/28 14时44分05秒
 * @author:  liujun (liujun0923@zju.edu.cn)
 * @breif:  
 */

#ifndef  PUB_INC
#define  PUB_INC

#include<stdio.h>

//定义一个回调函数,返回为int, 参数为int,int
typedef int(*myfoo)(int, int);

#endif   /* ----- #ifndef PUB_INC  ----- */


3.2、callback模块

头文件callback.h: 主要定义了一个结构体,包括a,b,回调函数foo。同时有一个执行函数process.

/***************************************************************************
 *
 * Copyright (c) 2014 liujun&&baidu. inc All Rights Reserved
 *
 **************************************************************************/

/*
 * @filename:  callback.h
 * @version:  1.0
 * @date : 2014/01/28 14时46分31秒
 * @author:  liujun (liujun0923@zju.edu.cn)
 * @breif:  
 */


#ifndef  CALLBACK_INC
#define  CALLBACK_INC

#include "pub.h"

typedef struct _foo_struct_t{
    int a;
    int b;
    myfoo foo;
}foo_struct_t;

int process(foo_struct_t* data);

#endif   /* ----- #ifndef CALLBACK_INC  ----- */


执行函数callback.c: 如果a=100,那么返回10000,否则使用回调函数来执行

/***************************************************************************
 *
 * Copyright (c) 2014 liujun&&baidu. inc All Rights Reserved
 *
 **************************************************************************/

/*
 * @filename:  callback.c
 * @version:  1.0
 * @date : 2014/01/28 14时48分26秒
 * @author:  liujun (liujun0923@zju.edu.cn)
 * @breif:  
 */

#include "callback.h"

int process(foo_struct_t* data)
{
	int res;
	//这里你可以做很多事情。

	//这里你可以用主函数传来的函数指针来进行回调
	if( data->a == 100)
	{
		res = 10000;
	}
	else
	{
		res =  data->foo(data->a, data->b);
	}
	//你还是可以做很多事情
	return res;
}


3.3、main模块

main.c: 从命令行接受参数a,b, 然后将a,b以及myfun函数地址一起参入模块callback。

/***************************************************************************
 *
 * Copyright (c) 2014 liujun&&baidu. inc All Rights Reserved
 *
 **************************************************************************/

/*
 * @filename:  main.c
 * @version:  1.0
 * @date : 2014/01/28 14时52分48秒
 * @author:  liujun (liujun0923@zju.edu.cn)
 * @breif:  
 */

#include "pub.h"
#include "callback.h"
#include <stdlib.h>

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

int main(int argc, char** argv)
{
    foo_struct_t* data = (foo_struct_t*)malloc(sizeof(foo_struct_t));
    data->a = atoi(argv[1]);
    data->b = atoi(argv[2]);
    data->foo = myfun;    
    int res = process(data);
    printf("after callback is %d\n", res);
    free(data);
    return 0;
}


3.4、Makefile

先生存libcallback,然后生成可执行文件。

main: main.c libcallback
	gcc -g main.c -I. -L. -lcallback -o main

libcallback:callback.h callback.c pub.h
	gcc -g -c callback.h callback.c pub.h
	ar -r libcallback.a callback.o

clean:
	rm *.gch
	rm *.o
	rm *.a
	rm main


3.5、执行结果

liujun@ubuntu:~/test/callback$ ./main  100 200
after callback is 10000
liujun@ubuntu:~/test/callback$ ./main  50 200
after callback is 250

可以看到达到我们预期效果。


最后给个具体的例子:

https://github.com/jungleliu0923/myserver

客户端C连接例子部分代码如下,其中mys_server_set_callback就是使用回调。

int main()
{
    my_log_init("./log", "sample.log", "sample.log.wf", 16);
    server = my_server_create("./conf/", "myserver.conf", "sample");
    if(server == NULL)
    {
        cout << "create sever fail\n";
        return -1;
    }
    my_server_set_callback(server, my_callback);
    my_server_run(server);
    return 0;
}



© 著作权归作者所有

共有 人打赏支持
jungleliu0923

jungleliu0923

粉丝 42
博文 12
码字总数 17323
作品 4
海淀
高级程序员
加载中

评论(36)

DylanWild
DylanWild
7979
战神_巫
战神_巫

引用来自“除美灭日平韩”的评论

没明白啥意思,用 data->foo(data->a, data->b);和直接用myfun((data->a, data->b);
这两个有什么区别?!

foo可以动态改变。
D
Dingo妹

引用来自“雷毅”的评论

楼主非常不错!然后我觉得有些混淆,有必要提下:
1、对“回调函数”的定义,你说成了更宽的“函数指针”。准确地说,回调函数的本质应该是:把一个函数作为参数传递到其它代码,待其调用。这一设计可以满足允许底层代码调用高层定义的子程序的需求。比方说,司令:“一师,你去负责巡逻钓鱼岛。如果遇到丫挺导弹攻击,你把方位告诉核弹军,由它们负责摧毁丫挺的”,这就意味着,一师遇到丫挺导弹攻击以后,把丫挺的位置作为参数调用核弹军。但是司令不能把核弹军的指挥权分配给一师,酱紫。
2、因此,qsort里的compar参数,符合“函数指针”范畴,不符合“回调函数”范畴。进一步说(特意搜索了一下)对“回调函数”的编辑,百度百科 http://baike.baidu.com/view/414773.htm 和你这个一样,错了;wikipedia的: http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 ,更准确
3、既然作为经验写给别人,就要注意语文水平
比如2节第三段“回调函数则不同。A模块定义一个回调函数C,将函数指针C传给B模块作为参数。在调用B模块之后,B模块会根据一定的条件触发回调C,进而重新调用模块A的函数。”,存在较严重的事物单元混淆,以及主语歧义,最后导致的是严重的文不符题。
又比如:接受≠接收
4、例子代码不够场景化。从 “3.5、执行结果” 还要回溯很久才能了解到场景,我已经回溯一分钟了还没明白。如果用我的例子来作演示程序,从下列日志一看就明白了:
腊月30 14:00 ,一师开始巡逻
腊月30 16:00 ,一师巡逻完毕返回本港,没有异常迹象
正月初一 14:00,一师开始巡逻
正月初一 14:30,一师发现丫挺在E127°N26°发射导弹过来
正月初一 14:31,核弹军收到一师通知出动,发射原子弹炸毁丫挺E127°N26°的导弹舰队
正月初一 15:00,一师巡逻钓鱼岛以东的战场遗迹
正月初一 17:00,一师巡逻完毕返回本港,带回遗迹视频2T,照片3G

79
jungleliu0923
jungleliu0923

引用来自“calvinwilliams”的评论

《开源纯C日志函数库iLOG3快速入门(二、定制远程日志服务).txt》
http://my.oschina.net/u/988092/blog/199698

《开源纯C日志函数库iLOG3快速入门(三、日志过滤和转档后压缩)》
http://my.oschina.net/u/988092/blog/200005

开源日志函数库iLOG3其实是实现了一个日志控制框架,通过大量回调函数钩子,你完全可以编写自己的函数来替代和扩展内部默认实现的功能,比如打开、输出、关闭日志回调函数钩子、日志过滤钩子和转档前后钩子。

恩。这个是一个非常经典的例子
calvinwilliams
calvinwilliams
《开源纯C日志函数库iLOG3快速入门(二、定制远程日志服务).txt》
http://my.oschina.net/u/988092/blog/199698

《开源纯C日志函数库iLOG3快速入门(三、日志过滤和转档后压缩)》
http://my.oschina.net/u/988092/blog/200005

开源日志函数库iLOG3其实是实现了一个日志控制框架,通过大量回调函数钩子,你完全可以编写自己的函数来替代和扩展内部默认实现的功能,比如打开、输出、关闭日志回调函数钩子、日志过滤钩子和转档前后钩子。
夜色谁占用了

引用来自“雷毅”的评论

楼主非常不错!然后我觉得有些混淆,有必要提下:
1、对“回调函数”的定义,你说成了更宽的“函数指针”。准确地说,回调函数的本质应该是:把一个函数作为参数传递到其它代码,待其调用。这一设计可以满足允许底层代码调用高层定义的子程序的需求。比方说,司令:“一师,你去负责巡逻钓鱼岛。如果遇到丫挺导弹攻击,你把方位告诉核弹军,由它们负责摧毁丫挺的”,这就意味着,一师遇到丫挺导弹攻击以后,把丫挺的位置作为参数调用核弹军。但是司令不能把核弹军的指挥权分配给一师,酱紫。
2、因此,qsort里的compar参数,符合“函数指针”范畴,不符合“回调函数”范畴。进一步说(特意搜索了一下)对“回调函数”的编辑,百度百科 http://baike.baidu.com/view/414773.htm 和你这个一样,错了;wikipedia的: http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 ,更准确
3、既然作为经验写给别人,就要注意语文水平
比如2节第三段“回调函数则不同。A模块定义一个回调函数C,将函数指针C传给B模块作为参数。在调用B模块之后,B模块会根据一定的条件触发回调C,进而重新调用模块A的函数。”,存在较严重的事物单元混淆,以及主语歧义,最后导致的是严重的文不符题。
又比如:接受≠接收
4、例子代码不够场景化。从 “3.5、执行结果” 还要回溯很久才能了解到场景,我已经回溯一分钟了还没明白。如果用我的例子来作演示程序,从下列日志一看就明白了:
腊月30 14:00 ,一师开始巡逻
腊月30 16:00 ,一师巡逻完毕返回本港,没有异常迹象
正月初一 14:00,一师开始巡逻
正月初一 14:30,一师发现丫挺在E127°N26°发射导弹过来
正月初一 14:31,核弹军收到一师通知出动,发射原子弹炸毁丫挺E127°N26°的导弹舰队
正月初一 15:00,一师巡逻钓鱼岛以东的战场遗迹
正月初一 17:00,一师巡逻完毕返回本港,带回遗迹视频2T,照片3G

不明觉厉!
ThinkingT
ThinkingT
简单的观察着.设计模式看看吧
jungleliu0923
jungleliu0923

引用来自“KevinJen”的评论

引用来自“jungleliu0923”的评论

引用来自“中山野鬼”的评论

引用来自“jungleliu0923”的评论

引用来自“Omar”的评论

main里malloc后要free.

程序执行完毕退出之后,内存都会被操作系统收回。所以这样写不会出现内存泄漏。我们常说的内存泄漏是程序执行过程中malloc但是没有free.

你这个思想可真心不对。哈。你今天能想到内存都会被操作系统收回,明天构造架构时,就很难主动思考malloc,free谁是责任主体,和关联主体之间的关联关系。哈。其实后者的分析很重要,独立的分析也经常被忽视。

恩。这个倒是的。我改了下代码

讲回调函数的忽略这些问题嘛,何况运行参数的个数没判断,申请内存没判断,回调函数讲的好就行了。

哈哈。既然朋友指出问题,严谨点肯定是好的。代码写的很粗糙,很多细节都没考虑到
KevinJen
KevinJen

引用来自“jungleliu0923”的评论

引用来自“中山野鬼”的评论

引用来自“jungleliu0923”的评论

引用来自“Omar”的评论

main里malloc后要free.

程序执行完毕退出之后,内存都会被操作系统收回。所以这样写不会出现内存泄漏。我们常说的内存泄漏是程序执行过程中malloc但是没有free.

你这个思想可真心不对。哈。你今天能想到内存都会被操作系统收回,明天构造架构时,就很难主动思考malloc,free谁是责任主体,和关联主体之间的关联关系。哈。其实后者的分析很重要,独立的分析也经常被忽视。

恩。这个倒是的。我改了下代码

讲回调函数的忽略这些问题嘛,何况运行参数的个数没判断,申请内存没判断,回调函数讲的好就行了。
jungleliu0923
jungleliu0923

引用来自“中山野鬼”的评论

引用来自“jungleliu0923”的评论

引用来自“Omar”的评论

main里malloc后要free.

程序执行完毕退出之后,内存都会被操作系统收回。所以这样写不会出现内存泄漏。我们常说的内存泄漏是程序执行过程中malloc但是没有free.

你这个思想可真心不对。哈。你今天能想到内存都会被操作系统收回,明天构造架构时,就很难主动思考malloc,free谁是责任主体,和关联主体之间的关联关系。哈。其实后者的分析很重要,独立的分析也经常被忽视。

恩。这个倒是的。我改了下代码
使用 acl 库编写高并发非阻塞网络通信程序

一、概述 acl 库的 C 库(libacl) 的 aio 模块设计了完整的非阻塞异步 IO 通信过程,在 acl 的C++库(libaclcpp) 中封装并增强了异步通信的功能,本文主要描述了 acl C++ 库之非阻塞IO库的设计...

郑树新
2014/08/25
0
0
为什么要使用extern "C"

C/C++采用的是分别编译模型, 源代码只要声明函数, 就可调用。 编译时,在函数调用处生成一个符号引用。 链接时,将函数调用处的符号引用,替换成地址(甚至仍有可能继续保留符号, 载入时再...

borey
2014/10/14
0
0
jni在C/C++代码中调用java函数,java函数的参数是接口,有办法吗?

先上一段安卓下面搜索BLE的代码: 这里面的this是一个回调接口,当搜索到蓝牙设备时,会回调该接口。 在安卓上面用java没问题,但是现在需要在C++函数中实现这一段。 步骤描述: C++: 1 获取...

lvrenyang
05/22
0
0
nodejs的C++扩展中实现异步回调

在nodejs的官方网站中有关于C++扩展的详细说明,其中包含了从"hello world"到对象封装的一系列示例。其中的“callback”节是关于回调函数的,美中不足的是,这个回调是阻塞的回调。 官方示例...

GZShi_alpha
2014/07/01
0
2
php数组array_filter()函数和array_slice()函数

<?php / arrayfilter()用回调函数过滤数组中的单元 arrayfilter(array,function) 参数描述:如果自定义过滤函数返回 true,则被操作的数组的当前值就会被包含在返回的结果数组中, 并将结果组成...

BearCatYN
2015/03/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

(三)Nginx配置·续

概述 前文写了关于Nginx环境配置,但是还没有完,接下来将会继续讲三个相关的配置 主要是以下三个 1.Nginx访问日志 2.Nginx日志切割 3.静态文件不记录日志和过期时间 Nginx访问日志 1.先看看...

杉下
今天
1
0
jquery创建类似于java的map

var map = {}; // Map map = new HashMap(); map[key] = value; // map.put(key, value); var value = map[key]; // Object value = map.get(key); var has = key in map; // boolean has = ......

SuperDabai
今天
0
0
java大数据转换16进制转10进制

public static void main(String[] args) {String hex = "0xdbf3accc683297cf0000";BigInteger amount = new BigInteger(hex.substring(2), 16);System.out.println(amount);......

任梁荣
昨天
2
0
OSChina 周六乱弹 —— 目测我们程序员丁克的几率不大

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @真Skr小机灵鬼儿:8.13分享Jocelyn Pook/Russian Red的单曲《Loving Strangers》 《Loving Strangers》- Jocelyn Pook/Russian Red 手机党少...

小小编辑
昨天
13
3
TypeScript基础入门 - 函数 - 剩余参数

转载 TypeScript基础入门 - 函数 - 剩余参数 项目实践仓库 https://github.com/durban89/typescript_demo.gittag: 1.2.1 为了保证后面的学习演示需要安装下ts-node,这样后面的每个操作都能...

durban
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部