在说回调函数与转移表之前,先看下什么叫做函数指针~
首先看两个非法的函数声明:
int fun()[];/***1***/
int fun[]();/***2***/
首先看第一个声明,函数的返回值是一个数组?这个本身就是不对的,函数只能返回标量。
再看第二个声明,函数名的是一个数组,数组元素必须要有相同的长度,不同的函数必然具有不同的长度。
再看看下面两个声明,这个是合法的:
int (*fun[])();
int *(*fun[])();
第一个声明,首先为括号内的*fun()求值,fun是个元素为某种类型的指针数组,运算完括号内之后后面为函数的调用;第二个声明是对其的一个间接访问调用。
为了说明这一点,下面举例。
这里需要注意的是函数指针进行间接访问前,必须对其进行初始化使其指向一个函数,这里的&是可选的,由于函数名被使用的时候总是由编译器把它转换为函数指针:
int func(int);
int (*pf)(int)=&func;
然后进行操作:
int result;
result=func(10);
result=(*pf)(10);
result=pf(10);
上面三种调用都是等价的,
下面来讲回调函数,首先看个例子,链表的检索:
Node *search(Node *linklist,int value)
{
while(!linklist){
if(linklist->value==value)
break;
else
linklist=linklist->next;
}
return linklist;
}
上面就是简单的链表查询的一个函数,这里查找的int类型的元素,但是一旦我们的链表是其他类型的元素,需要修改链表查询的程序,可能改动超大。怎么能做到不改变这个程序而做到呢?首先想到的是要屏蔽查询元素的类型,比较元素的时候调用函数进行,这样就可以实现对任意类型的链表查询而不用修改链表查询程序。这里的比较函数的类型可能有多种,函数名的长度不定,这里就需要我们使用函数指针。
修改掉比较的问题之后,接下来就是查找函数调用的时候怎么传值。这里采用指向值得指针而非值得本身,这里使用一个void *形参,用于接受这个参数,然后传过来的指针传递给比较函数,使用指针类型进行传值,是函数的功能更加强大,可以接受字符串、数组等变量。
描述完对上面函数的修改后,我们会发现,我们把一个函数指针作为参数传递给函数,然后函数“回调”函数指针指向的用户函数,这样的函数就称为回调函数(callback function)。
修改完的函数如下:
Node *search(Node *linklist,void const *value,int *compare(void const *,void const *))
{
while(!linklist)
{
if(!compare(&linklist->value,value))
break;
else
linklist=linklist->next;
}
return linklist;
}
int compare_int(void const *a,void const *b)
{
if(*(int *)a==*(int *)b)
return 0;
else
return 1;
}
调用上面的函数的时候,可以这样进行:
search(linklist,&value,compare_int);
search(linklist,"hello world",strcmp);
在实际的应用中,我们可以根据具体的需求进行compare函数的修改,适应自己的需求。
上面所讲就是回调函数,下面介绍另一个概念“转移表”。
一看转移这几个字儿,就想到和状态相关,我们在实际应用中使用if-else结构或者switch语句进行一些状态的切换。但是如果遇到比较复杂情况,转移次数达到数百次或者数千次,如果再使用if-else结构或者switch语句,维护起这个软件系统,工作量将会相当大。这个时候可以采用“转移表”来避免这个情况。比如实现一个计算器的程序,使用switch语句实现如下:
switch(operation)
{
case ADD:
result=add(a,b);break;
case SUB:
result=sub(a,b);break;
case MUL:
result=mul(a,b);break;
case DIV:
result=div(a,b);break;
.....
}
如前面所言,如果这个计算器要实现的功能很多,那么将有很多这样的语句,可维护性很差。如果我们将具体的数值操作与选择操作的代码分开将会提高代码的可读性。这种情况下,我们需要建立一个“转移表”。在建立转移表之间需要对涉及到的函数提前声明,然后建立转移表,对于上面的可以这么修改:
double add(double,double);
double sub(double,double);
double mul(double,double);
double div(double,double);
......
那么建立的转移表如下:
double (*operation_fun[])(double,double)={add,sub,mul,div,......};
在调用的时候可以这样操作:
double result;
result=operation_fun[operation](a,b);
上面两句可以替换switch语句。