簡體   English   中英

二進制樹的通用C

[英]Generic C for binary trees

如何編寫通用c?

我開始寫一組平衡的樹木(替罪羊,八卦,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函數是完全有可能的,並且相當容易。

一種技巧是使用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 /一個/816536分之10430893

另請參閱以下答案:

這是一個通用樹破壞的示例:

// 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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM