在C中为链表编写通用搜索功能?

时间:2016-09-18 15:11:03

标签: c function-pointers

我正在尝试在C中的链表上编写通用搜索功能。 功能如下:

void* get_when_true(linked_list *l,
                    int (*condition)(const void*)) {
        node **walk = &l->head;
        while (*walk != NULL && !condition((*walk)->data)) {
                walk = &((*walk)->next);
        }
        return (*walk)->data;
}

基本上 condition 是一个函数指针,该函数将针对链表的每个元素进行测试,每当此函数返回true时,将返回特定节点。

我的链表中的每个节点都存储了一些ID。我的问题是,如果我不想搜索特定的ID,我必须编写一个单独的搜索功能,只能检查该特定的ID。基本上如果我不想寻找ID 10.我必须写一个函数

int isID_10(const void *a) {
    // cast A to appropriate poitner
    return a->ID == 10;
}

如果我想测试其他一些说30的ID,我将不得不再写一个不同的功能..我想你明白了我的意思。

我只能考虑currying来解决这个问题..但不幸的是C不提供currying,我不能使用一些特定于编译器的工作,因为我的代码应该在所有平台上编译。

使用全局变量作为搜索ID是一种解决方案,但我不想避免它。

如何解决这个问题?

1 个答案:

答案 0 :(得分:3)

您应该向get_when_true添加另一个传递给回调的参数。这可以只是另一个void *,因此它可以指向任何类型的结构。

void* get_when_true(linked_list *l, void *ctx,
                    int (*condition)(const void* data, void *ctx)) {
        node **walk = &l->head;
        while (*walk != NULL && !condition((*walk)->data, ctx)) {
                walk = &((*walk)->next);
        }
        return (*walk)->data;
}

这是一个回调:

int isID(const void *data, void *ctx) {
    int id = (int)ctx;
    const struct whatever *a = data;
    return a->ID == id;
}

像这样使用:

get_when_true(list, (void*)10, isID);

在这里,我甚至不需要一个结构,所以我"欺骗"然后将单个整数转换为void*

由于C本身不支持闭包,因为有数据附加"对于(回调)函数指针,我编写的任何接受回调的函数也需要传递给回调的void*

虽然C不支持闭包,但有一个GNU扩展提供了嵌套函数,可以访问父函数范围内的变量。因此,使用您的原始代码(没有我的附加参数):

int get_when_id(linked_list *l, int id) {
    int condition(const void *data) {
        const struct whatever *a = data;
        return a->ID == id;  // id is "closed over" from parent
    }

    return get_when_true(list, condition);
}

这需要可执行堆栈。如果你看一下反汇编,你会发现实际传递给get_when_true的函数指针不是condition本身的地址。而是传递堆栈上生成的可执行文件 thunk 的地址。这个thunk将指向封闭变量的指针传递给实际的condition函数,因此它们会自动在那里可用。

另见: