要详细的了解回调函数,需要预先了解typedef常见用法,对函数指针的了解。然后才是回调函数。

下面我们先介绍预备知识 typedef的用法和函数指针的学习。


预先准备一:


typedef常见用法

1.常规变量类型定义


例如:typedef unsigned char uchar

描述:uchar等价于unsigned char类型定义 uchar c声明等于unsigned char c声明


2.数组类型定义

例如: typedef int array[2];

描述: array等价于 int [2]定义; array a声明等价于int a[2]声明


扩展: typedef int array[M][N];

描述: array等价于 int [M][N]定义; array a声明等价于int a[M][N]声明


3.指针类型定义

例如: typedef int *pointer;

描述: pointer等价于 int *定义;pointer p声明等价于int *a声明


例如: typedef int *pointer[M];

描述: pointer等价于 int *[M]定义 pointer p声明等价于int *a[M]声明明

pointer 定义指向int型指针的数组,长度为M。

4.函数地址说明

描述:C把函数名字当做函数的首地址来对待,我们可以使用最简单的方法得到函数地址

例如: 函数:int func(void); unsigned long funcAddr=(unsigned long)func, funcAddr的值是func函数的首地址


5.函数声明

例如: typedef int func(void); func等价于 int (void)类型函数

描述1: func f声明等价于 int f(void)声明,用于文件的函数声明

描述2: func *pf声明等价于 int (*pf)(void)声明,用于函数指针的生命,见下一条


6.函数指针

例如: typedef int (*func)(void)

描述: func等价于int (*)(void)类型

func pf等价于int (*pf)(void)声明,pf是一个函数指针变量


7.识别typedef的方法:

a).第一步。使用已知的类型定义替代typdef后面的名称,直到只剩下一个名字不识别为正确

如typedef u32 (*func)(u8);

从上面的定义中找到 typedef __u32 u32;typedef __u8 u8

继续找到 typedef unsigned int __u32;typedef unsigned char __u8;

替代位置名称 typedef unsigned int (*func)(void);

现在只有func属于未知。

b).第二步.未知名字为定义类型,类型为取出名称和typedef的所有部分,如上为

func等价于unsigned unsigned int (*)(unsigned char);

c).第三部.定义一个变量时,变量类型等价于把变量替代未知名字的位置所得到的类型

func f等价于unsigned unsigned int (*f)(unsigned char)


到此typedef基本掌握。下面进行函数指针的学习。


预先准备二:


函数指针的一些了解


1.简单的函数指针的应用

形式1:返回类型(*函数名)(参数表) 


char (*pFun)(int); 

char glFun(int a){ return;} 

void main() 

    pFun = glFun; 

    (*pFun)(2); 

}


第一行定义了一个指针变量pFun。首先我们根据前面提到的“形式1”认识到它是一个指向某种函数的指针,这种函数参数是一个int型,返回值是char类型。只有第一句我们还无法使用这个指针,因为我们还未对它进行赋值。

第二行定义了一个函数glFun()。该函数正好是一个以int为参数返回char的函数。我们要从指针的层次上理解函数——函数的函数名实际上就是一个指针,函数名指向该函数的代码在内存中的首地址

然后就是main()函数了,它的第一句您应该看得懂了——它将函数glFun的地址赋值给变量pFun。main()函数的第二句中“*pFun”显然是取pFun所指向地址的内容,当然也就是取出了函数glFun()的内容,然后给定参数为2。


2.使用typedef更直观更方便

形式1:typedef  返回类型(*新类型)(参数表)


typedef char (*PTRFUN)(int); 

PTRFUN pFun; 

char glFun(int a){ return;} 

void main() 

    pFun = glFun; 

    (*pFun)(2); 


typedef的功能是定义新的类型。

第一句就是定义了一种PTRFUN的类型,并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回char类型。后面就可以像使用int,char一样使用PTRFUN了。

第二行的代码便使用这个新类型定义了变量pFun,此时就可以像使用形式1一样使用这个变量了。


3.例子说明


#include <stdio.h>

#include <assert.h>


typedef int (*FP_CALC)(int,int);//定义一个函数指针类型


int add(int a, int b)

{

return a + b;

}


int sub(int a, int b)

{

return a - b;

}


//定义一个函数,参数为op,返回一个指针,该指针类型为拥有两个int参数、

//返回类型为int的函数指针。它的作用是根据操作符返回相应函数的地址

FP_CALC calc_func(char op)

{

switch( op )

{

case '+':

return add;

case '-':

return sub;

default:

return NULL;

}

return NULL;

}


//s_calc_func为函数,它的参数是 op,   

//返回值为一个拥有两个int参数、返回类型为int的函数指针  

int (*s_calc_func(char op)) (int , int)

{

return calc_func(op);

}


//最终用户直接调用的函数,该函数接收两个int整数,

//和一个算术运算符,返回两数的运算结果

int calc(int a, int b, char op)

{

FP_CALC fp = calc_func(op);

int (*s_fp)(int,int) = s_calc_func(op);//用于测试


assert(fp == s_fp);//这两个是相等的


if(fp)

return fp(a,b);

else

return -1;

}


void main()

{

int a = 100, b = 20;


printf("calc(%d, %d, %c) = %d\n", a, b, '+', calc(a, b, '+'));

printf("calc(%d, %d, %c) = %d\n", a, b, '-', calc(a, b, '-'));   

}


到此对函数指针有了一定的掌握。现在进入正题回调函数的学习。


正题:

回调函数的学习

  1. 何为回调(callback)

所谓回调,就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。例如Win32下的窗口过程函数就是一个典型的回调函数。

    一般说来,C不会自己调用B,C提供B的目的就是让S来调用它,而且是C不得不提供。由于S并不知道C提供的B叫甚名谁,所以S会约定B的接口规范(函数原型),然后由C提前通过S的一个函数Register告诉S自己将要使用B函数,这个过程称为回调函数的注册,Register称为注册函数

下面举个通俗的例子:

    某天,我打电话向你请教问题,当然是个难题,:),你一时想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。过了XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。故事到此结束。

 这个例子说明了“异步+回调”的编程模式。其中,你后来打手机告诉我结果便是一个“回调”过程;我的手机号码必须在以前告诉你,这便是注册回调函数;我的手机号码应该有效并且手机能够接收到你的呼叫,这是回调函数必须符合接口规范。


    平时我们自己编写程序时也可能用到回调机制,这时,我们既是回调的设计者又是回调的使用者。

2. 例子

回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此,要实现回调,必须首先定义函数指针,请看下面的例子:

void Func(char *s);// 函数原型

void (*pFunc) (char *);//函数指针

可以看出,函数的定义和函数指针的定义非常类似。

一般的化,为了简化函数指针类型的变量定义,提高程序的可读性,我们需要把函数指针类型自定义一下。

typedef void(*pcb)(char *);

回调函数可以象普通函数一样被程序调用,但是只有它被当作参数传递给被调函数时才能称作回调函数。

被调函数的例子:

void GetCallBack(pcb callback)

{

/*do something*/

}

用户在调用上面的函数时,需要自己实现一个pcb类型的回调函数:

void fCallback(char *s) 

{

/* do something */

然后,就可以直接把fCallback当作一个变量传递给GetCallBack,

GetCallBack(fCallback);

如果赋了不同的值给该参数,那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。


参考网址:

http://blog.csdn.net/zxncvb/article/details/6714924

http://blog.chinaunix.net/uid-8318378-id-2032233.html

http://blog.csdn.net/wubin1124/article/details/4386269

http://www.cnblogs.com/this-543273659/archive/2011/07/20/2111815.html