在 C 中为链表编写通用搜索函数?

Posted

技术标签:

【中文标题】在 C 中为链表编写通用搜索函数?【英文标题】:Writing generic Search function for linked list in C? 【发布时间】:2016-09-18 15:11:02 【问题描述】:

我正在尝试在 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;

如果我想测试其他 ID,比如 30,我将不得不再次编写不同的函数。我想你明白了。

我只能想到用柯里化来解决这个问题。但不幸的是,C 不提供柯里化,而且我不能使用一些特定于编译器的变通方法,因为我的代码应该在所有平台上编译。

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

如何解决这个问题?

【问题讨论】:

但是明天如果我使用我的链表来存储一些结构、整数和字符并且我想比较它们,那么结构格式将会改变,我的 get_when_true 函数将不再保持通用 【参考方案1】:

您应该向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 函数,因此它们会自动在那里可用。

另见:

Is there a a way to achieve closures in C

【讨论】:

谢谢你提到闭包和空指针的关系! 不客气。我花了很长时间才意识到这两者是直接相关的。请参阅我使用 GCC 的嵌套函数的附加示例。 我不能推荐 GCC 的嵌套函数。其他一切都加一个;减一——净零。

以上是关于在 C 中为链表编写通用搜索函数?的主要内容,如果未能解决你的问题,请参考以下文章

在 c 中为链表创建结构时出现错误 C2061

Leetcode——二叉树展开为链表 / 将有序数组转换为二叉搜索树 / 有序链表转换二叉搜索树

C 编程为链表类型结构分配空间

4.3 为链表设置虚拟头结点dummyhead

leetcode 二叉树展开为链表 中等

c语言如何从文件读入,并存放在链表中