【问题标题】:Generic C for binary trees二叉树的通用 C
【发布时间】:2015-05-19 17:42:41
【问题描述】:

如何编写泛型 c?

我开始编写一系列平衡树(替罪羊、splay、aa 等),并发现许多共同点。示例是下面的破坏函数。

这样的函数和类似的函数是否可以用 void 指针定义而不会导致“取消引用 void* 指针错误”?

销毁函数示例

void splay_node_linked_destroy(SplayNode **this) {
 72   if(*this == NULL) {
 73     return;
 74   }
 75   SplayNode *root = (*this)->root, *previous, *next;
 76   while(root) {
 77     if(previous == root->parent) {
 78       // todo use funcs for comparisons for generic
 79       if(root->left) {
 80         next = root->left;
 81       } else if(root->right) {
 82         next = root->right;
 83       } else {
 84         next = root->parent;
 85       }
 86     } else if(previous == root->left) {
 87       if(root->right) {
 88         next = root->right;
 89       } else {
 90         next = root->parent;
 91       }
 92     } else {
 93       next = root->parent;
 94     }
 95     previous = root;
 96     if(next == root->parent) {
 97       splay_node_destroy(&root);
 98       // todo use callback here to make generic
 99     }
100     root = next;
101   }
102 }

【问题讨论】:

  • 一般来说,遗憾的是,答案是否定的。至少在标准 C 中,如果你不使用 UB...

标签: c generics search binary-tree binary-search-tree


【解决方案1】:

实际上,编写处理给定算法的“任意”(通用)数据类型的 C 函数是完全可能的,而且相当容易。

一个技巧是使用void 指针并以这样一种方式设计API,即使用包装函数将指针转换为它们的实际类型,从而受益于C 允许的尽可能多的类型检查和安全性。唯一困难的部分是编译器通常不会帮助您将类型信息传递给您的实现,尽管一些编译器确实支持 typeof() 扩展,这使得编写为您完成这项工作的包装宏成为可能。

一些例子:

这个例子似乎以我建议的方式使用typeof()https://github.com/troydhanson/uthash/tree/master/src

这是一个有趣的基于预处理器宏的库,灵感来自标准模板库:http://sglib.sourceforge.net/

这可能是 C 中泛型类型的泛型算法最完整的示例之一,尽管它有点丑陋且非常冗长,而且可能效率不高:http://home.gna.org/gdsl/

这个答案提供了很好的建议,尽管它使用嵌入式 union 而不是 void 指针。如果您提前知道所有可能的数据类型,union 是理想的选择:
https://stackoverflow.com/a/2891570/816536

从通用数据结构构建高级结构(如列表)的另一种有趣方法是可以被描述为由内向外的技术(我也喜欢这种技术!)。在 4.4BSD queue(3)tree(3) 宏中可以找到我认为该由内而外技术的规范实现,这里有一些更易读的解释和示例:

此答案描述了一种高度依赖于预处理器的技术,尽管它要求您提前知道所有对象类型将是什么,或者强制用户为其特定数据类型编写中间标头:https://stackoverflow.com/a/10430893/816536

另请参阅以下答案:

【讨论】:

    【解决方案2】:

    这是一个通用树销毁的示例:

    // GenericTree.h
    
    void genericTreeDestroy(void *treeNode,  void (*fUserFree)(void *));
    
    // GenericTree.c
    typedef struct TREE_NODE {
        struct TREE_NODE *left;
        struct TREE_NODE *right;
    };
    void genericTreeDestroy(struct TREE_NODE *treeNode,  void (*fUserFree)(void *))
    {
        if (treeNode->left)  genericTreeDestroy(treeNode->left,  fUserFree);
        if (treeNode->right) genericTreeDestroy(treeNode->right, fUserFree);
        if (fUserFree) fUserFree(treeNode);
        free(treeNode);
    }
    
    // UserStuff.c
    #include "GenericTree.h"
    typedef struct MY_TREE_NODE {
        struct MY_TREE_NODE *left;
        struct MY_TREE_NODE *right;
        int some_value;
        char *name;
    };
    void my_freedata(struct MY_TREE_NODE *node);
    
    void main(void)
    {
        struct MY_TREE_NODE *myTree= calloc(1,sizeof(struct MY_TREE_NODE));
        myTree->name= malloc(strlen("Hello world")+1);
        genericTreeDestroy(myTree, my_freedata);
    }
    
    void my_freedata(struct MY_TREE_NODE *node)
    {
        free(node->name);
    }
    

    诀窍是所有树都必须从左右成员开始。 .h 文件使用void * 参数定义genericTreeDestroy,.c 文件将其定义为struct TREE_NODE *。通过这种方式,用户可以将任何树节点类型传递给它。

    接下来用户可以定义任何树节点类型(只要它具有左右成员)并调用通用函数来销毁它。可以为泛型函数指定一个函数来对用户定义的节点类型进行任何清理,如果不需要,则为 null。

    您可以用同样的方式定义其他函数。这是一个搜索功能:

    // .h
    void *generic_tree_search (void *tree, void *value, int (*fUserCmp)(void *value, void *node));
    
    // .c
    void *generic_tree_search (struct TREE_NODE *treeNode, void *value, int (*fUserCmp)(void *value, void *node))
    {
        while (treeNode) {
            switch (fUserCmp(value,treeNode) {
            case -1: if (treeNode->left) treeNode= treeNode->left; else return(0); break;
            case  0: return(treeNode);
            case +1: if (treeNode->right) treeNode= treeNode->right; else return(0); break;
            }
        }
        return(0);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-17
      相关资源
      最近更新 更多