[英]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.