//新增元素 HASH_ADD_INT(head, keyfield_name, item_ptr); HASH_ADD_STR(head, keyfield_name, item_ptr); HASH_ADD_PTR(head, keyfield_name, item_ptr); //查找元素 HASH_FIND_INT(head, key_ptr, item_ptr); HASH_FIND_STR(head, key_ptr, item_ptr); HASH_FIND_PTR(head, key_ptr, item_ptr); //删除元素 HASH_DEL(head, item_ptr); //返回长度 HASH_COUNT(head); //释放堆区内存 HASH_CLEAR(hh_name, head); //排序,cmp_func书写规则和qsort一样。 HASH_SORT(head, cmp_func);
参数介绍:
head
: 哈希表变量,是一个结构体指针
keyfield_name
: 结构体内键的定义名称
item_ptr
: 新增节点的指针
key_ptr
: 键变量的指针
hh_name
: UT_hash_handle变量的定义名称
上面列出了常用的库函数(都是便利版本,如果想要更高的灵活性请参考原文链接uthash)
正常来说,上面这些就足够刷题使用了。
下面是uthash的使用步骤:
typedef struct { int key; char name[20]; uint8_t age; UT_hash_handle hh; } HashMap; static HashMap *head = NULL;
这里必须的只有两个,一个是UT_hash_handle hh;
,映射就是通过这个宏实现的,变量名称最好定义为hh
,否则上面介绍的便利版本宏函数全部用不了(便利版本内部默认使用hh导致)。当然定义成别的名称也不是不行,函数就得用通用版本,参数更多也更难记,总不能既要有要还要吧~
另一个就是key变量,便利版本支持三种类型:int
,char *
,void *
,查找会用到该字段。
最后声明一个全局变量head,他就是hash表的入口,初始化必须是NULL。所有函数都会用到他。
HashMap *Find(const int key) { HashMap *node = NULL; HASH_FIND_INT(head, &key, node); //如果是int,要取地址,str或ptr不需要 return node; } void Add(const int key, const char *name, const uint8_t age) { HashMap *node = Find(key); if (!node) { node = (HashMap *)malloc(sizeof(HashMap)); node->key = key; node->age = age; strcpy_s(node->name, sizeof(node->name), name); HASH_ADD_INT(head, key, node); //注意,这里key其实是结构体主键的定义名称,而不是变量。 } } void Delete(const int key) { HashMap *node = Find(key); if (node) { HASH_DEL(head, node); free(node); //最好free一下,毕竟节点删掉了。 } }
为什么要定义这些呢?
很简单,为了代码可读,为了少写点代码字数(C大概是所有现代语言里最繁琐的一种了),具体看下文主函数的内容就知道了。
接下来简单说下。
int main() { Add(0, "zhang san", 18); //这里一条就可以加一个映射关系 Add(1, "li si", 21); Add(2, "wang wu", 31); for (int i = 0; i < HASH_COUNT(head); i++) { printf("name: %s age: %d\n", Find(i)->name, Find(i)->age); } HASH_CLEAR(hh, head); //必须,千万别忘了写 return 0; }
打印结果为:
name: zhang san age: 18 name: li si age: 21 name: wang wu age: 31
这里要特别注意,最后必须要调用HASH_CLEAR(hh, head);
,否则堆内存未被释放,在线编程会导致内存错误或结果不正确。
到这里,库函数和简单演示都结束了~
这个hash库可以做很多事情,因为他使用自定义结构体做节点,结构体里可以塞各种变量,主键和结构体会产生映射关系。
如果只有主键,那么就是个set;
如果除了主键还有其他结构体元素,那他就是个map;
如果Add函数不查重,那么他就是multi版本的set和map;
默认情况下是无序的,如果sort一下,他就是有序的。
这么看来,它就把C++里的8种set和map都覆盖了呢~