最近心特别浮躁,还没到晚上十点,就安定不下来了,但是这篇博文还必须记完!

上篇博文:函数指针,对这篇博文的理解也比较重要。

《c 与 指针》对于回调函数讲的也比较细致了,耐心看完,也会收获颇多,学习完毕,我简单地记录下吧:

以一个案例引入主题:

下面是一个在单链表中查找一个值的简单的函数。它的参数是一个指向链表第一个节点的指针以及那个需要查找的值。

Node *search_list( Node *node, int const value )
{
    while( node != NULL )
    {
        if( node->value == value )
            break;
        node = node->link;
    }
    return node;

}

这个函数特别简单,所以让人感觉到很亲切,至少对于我这种菜鸟来说,是亲切感爆棚。

可是呢?现实很残忍,我们必须开发出功能更加强大的查找链表中值的函数,不光是链表,其他领域也行。

怎么个强大法呢?在这里具体而言,就是上述函数只能处理这样的单链表——链表中的值为整数。我们想要一个这样的函数,它可以处理各种类型的值,例如我可以在一个字符串链表中查找!

我们就想一个通用的方法来使得查找函数与类型无关,这样它就能用于任何类型的值的链表。

这是我们就用到了回调函数这样一个概念。

就在上述函数的基础上修改吧。我们必须对函数两个方便进行修改:

第一个方面是,我们必须改变比较的执行方式,使函数可以对任何类型的值进行比较。如何实现,就是使用函数指针来间接访问一个比较函数,(由这个比较函数代替上述代码中的 ==),这个比较函数由你自己定义,这样就比较自由了,下面我们定义一个比较函数看看:

int compare_ints( void const *a, void const *b )
{
    if( *(int *)a == *(int *)b )
        return 0;
    else
        return 1;
}

这个比较函数是对整型值进行比较的,如果对其他类型的值进行比较,只需要改变强制类型转换的类型就可以了。注意,也要把函数名也改下,要不然,暗示作用太强。

这个函数的使用在这里是用一个指向该函数的指针作为参数传递给查找函数。然后查找函数调用这个函数来执行值的比较。使用这种方法,任何类型的值都可以进行比较。

必须修改的第2个方面是向函数传递一个指向值的指针而不是值本身。函数有一个void * 形参,用于接收这个参数。然后指向这个值的指针便传递给比较函数。

使用这个技巧的函数称为回调函数(callback function),因为用户把一个函数指针(指向比较函数的指针)作为参数传递给其他函数(查找函数),后者(查找函数)将“回调”用户的函数(比较函数)。任何时候,如果你所编写的函数必须能够在不同的时刻执行不同类型的工作或者执行只能由函数调用者定义的工作,你都可以使用这个技巧。

我们无法在这个上下文环境中为回调函数编写一个准确的原型,因为我们并不知道进行比较的值的类型。事实上,我们需要查找函数能作用于任何类型的值。解决这个难题的方法是把参数类型声明为 void *,表示一个“指向未知类型的指针”。

下面是与类型无关的链表查找函数:

#include <stdio.h>
#include "node.h"

Node *search_list( Node *node, void const *value, 
                  int (*compare)( void const *, void const * ) )
{
    while( node != NULL )
    {
        if( compare( &node->value, value ) == 0 )
            break;
        node = node->link;
    }
    
    return node;
}

最后讲讲这个函数是如何被调用的:

desired_node = search_list(root, &desired_value, compare_ints );

这个调用用来查找整型链表中的某个节点的值。

如果希望在一个字符串链表中进行查找呢?

#include <string.h>

...

desired_node = search_list(root, "desired_value", strcmp );

碰巧,库函数strcmp所执行的比较和我们需要的一样,不过编译器会发出警告,因为它的参数被声明为char *而不是 void *。

不过没关系,如果你需要比较字符串的话,确实传递进去的参数就是char *,且void *可以转换为任何其他类型的指针。