1 uthash 简介
uthash 哈希的实现很简单,它不是一个库,只是一个头文件,使用的时候incluede进去就行了
#include "uthash.h"
关于 uthash 的官方文档 :
https://troydhanson.github.io/uthash/userguide.html#_a_hash_in_c
下载头文件源码:
GitHub - troydhanson/uthash: C macros for hash tables and more
uthash 支持哈希表的如下操作:
-
add/replace
-
find
-
delete
-
count
-
iterate
-
sort
2 uthash 的使用
2.1 创建自己的哈希结构
以定义一个 学生信息为例,使用学号id 当做哈希表中的key,这个key是唯一的,类型也可以是任意的类型,这里根据需要定义为int类型,name也是根据需要自定义的,也可以是其它类型的,也可以不定义。
#include "uthash.h"
struct my_struct {
int id; /* key , unique , can be any type, char/pointer,etc */
char name[10]; /* can not use */
UT_hash_handle hh; /* makes this structure hashable */
};
2.2 声明哈希表
注意,这个声明一个全局变量,哈希表的头,一定要初始化为NULL
struct my_struct *users = NULL; /* important! initialize to NULL */
如果不想定义全局变量,想要以参数的形式,需要注意,一定要使用二重指针,不然是不正确的。
这部分官方文档有说明:
/* bad */
void add_user(struct my_struct *users, int user_id, char *name) {
...
HASH_ADD_INT(users, id, s);
}
/* good */
void add_user(struct my_struct **users, int user_id, char *name) { ...
...
HASH_ADD_INT(*users, id, s);
}
2.3 哈希表中 add item
如果key是 int 类型, HASH_ADD_INT(head,intfield,add)
如果key是 string 类型,HASH_ADD_STR(head,strfield,add)
如果key是 指针 类型, HASH_ADD_PTR(head,ptrfield,add)
如果key是 结构体 类型 HASH_ADD
具体的使用方法,可以参考 uthash.h 宏定义,实际上都是使用的 HASH_ADD,
可以参考如下定义
#define HASH_ADD_INT(head,intfield,add)
HASH_ADD(hh,head,intfield,sizeof(int),add)
这里key是 int 类型,这里会用到 HASH_ADD_INT(users, id, s) ,这里注意:
- 参数 users 是我们定义的哈希表的头
- 参数 id 是我们自定义哈希表结构中key 域的变量名称
- 参数 s 是我们要添加的item
void add_user(int user_id, char *name) {
struct my_struct *s;
s = malloc(sizeof *s);
s->id = user_id;
strcpy(s->name, name);
HASH_ADD_INT(users, id, s); /* id: name of key field */
}
2.4 哈希表中 delete item
删除哈希表中的item,需要知道指向它的指针,如果只知道它的key, 可以先通过 HASH_FIND 找到这个item,然后再删掉它。
void delete_user(struct my_struct *user) {
HASH_DEL(users, user); /* user: pointer to deletee */
free(user); /* optional; it's up to you! */
}
删除哈希表中所有item
void delete_all() {
struct my_struct *current_user, *tmp;
HASH_ITER(hh, users, current_user, tmp) {
HASH_DEL(users, current_user); /* delete; users advances to next */
free(current_user); /* optional- if you want to free */
}
}
2.5 哈希表中 iterating item
遍历 Iterating
struct my_struct *_user;
for(_user=users; _user!= NULL; _user=(my_struct *)(_user->hh.next)) {
printf("user %d, name %s\n", user->id, user->name);
}
或者
struct my_struct *current_user, *tmp;
HASH_ITER(hh, users, current_user, tmp) {
printf("user %d, name %s\n", current_user->id, current_user->name);
}
2.6 哈希表中 find item
需要根据 key 来 查找哈希表中的元素,然后调用 HASH_FIND 来查找。
如果key是 int 类型, HASH_FIND_INT(head, findint, out)
如果key是 string 类型,HASH_ADD_STR(head, findstr, out)
如果key是 指针 类型, HASH_ADD_PTR(head, findprt, out)
具体的使用方法,可以参考 uthash.h 宏定义,实际上都是使用的 HASH_FIND .
The middle argument is a pointer to the key ,第二个参数是指向key的指针。
如果找到了,找到的结构体hash item会指向 out ,如果找不到,out = NULL.
这里key 是整型的,可以调用 HASH_FIND_INT 来查找。
struct my_struct *find_user(int user_id) {
struct my_struct *s;
HASH_FIND_INT(users, &user_id, s); /* s: output pointer */
return s;
}
/*
Here, the hash table is users, and &user_id points to the key
(an integer in this case). Last, s is the output variable of HASH_FIND_INT.
The final result is that s points to the structure with the given key,
or is NULL if the key wasn’t found in the hash.
*/
2.7 哈希表中 count item
哈希表中存在item的数量可以用 HASH_COUNT来获取
The number of items in the hash table can be obtained using HASH_COUNT
:
unsigned int num_users;
num_users = HASH_COUNT(users);
printf("there are %u users\n", num_users);
2.7 哈希表中 sort item
哈希表默认是无序的,存储的顺序就是你插入item的顺序。如果在哈希表中有存储顺序的需求。
可以使用 HASH_SORT 来重新排序。
HASH_SORT(users, name_sort);
name_sort 是一个指向比较函数的函数指针,这个函数可以根据自己需求实现,但是必须满足一下要求(the same convention used by strcmp
or qsort
in the standard C library):
- accept two pointer arguments (the items to compare)
- must return an int
which is less than zero, zero, or greater than zero
int sort_function(void *a, void *b) {
/* compare a to b (cast a and b appropriately)
* return (int) -1 if (a < b)
* return (int) 0 if (a == b)
* return (int) 1 if (a > b)
*/
}
这里举两个例子:
int by_name(const struct my_struct *a, const struct my_struct *b) {
return strcmp(a->name, b->name);
}
int by_id(const struct my_struct *a, const struct my_struct *b) {
return (a->id - b->id);
}
void sort_by_name() {
HASH_SORT(users, by_name);
}
void sort_by_id() {
HASH_SORT(users, by_id);
}
3 完整例子
#include <stdio.h> /* printf */
#include <stdlib.h> /* atoi, malloc */
#include <string.h> /* strcpy */
#include "uthash.h"
struct my_struct {
int id; /* key */
char name[21];
UT_hash_handle hh; /* makes this structure hashable */
};
struct my_struct *users = NULL;
void add_user(int user_id, const char *name)
{
struct my_struct *s;
HASH_FIND_INT(users, &user_id, s); /* id already in the hash? */
if (s == NULL) {
s = (struct my_struct*)malloc(sizeof *s);
s->id = user_id;
HASH_ADD_INT(users, id, s); /* id is the key field */
}
strcpy(s->name, name);
}
struct my_struct *find_user(int user_id)
{
struct my_struct *s;
HASH_FIND_INT(users, &user_id, s); /* s: output pointer */
return s;
}
void delete_user(struct my_struct *user)
{
HASH_DEL(users, user); /* user: pointer to deletee */
free(user);
}
void delete_all()
{
struct my_struct *current_user;
struct my_struct *tmp;
HASH_ITER(hh, users, current_user, tmp) {
HASH_DEL(users, current_user); /* delete it (users advances to next) */
free(current_user); /* free it */
}
}
void print_users()
{
struct my_struct *s;
for (s = users; s != NULL; s = (struct my_struct*)(s->hh.next)) {
printf("user id %d: name %s\n", s->id, s->name);
}
}
int by_name(const struct my_struct *a, const struct my_struct *b)
{
return strcmp(a->name, b->name);
}
int by_id(const struct my_struct *a, const struct my_struct *b)
{
return (a->id - b->id);
}
const char *getl(const char *prompt)
{
static char buf[21];
char *p;
printf("%s? ", prompt); fflush(stdout);
p = fgets(buf, sizeof(buf), stdin);
if (p == NULL || (p = strchr(buf, '\n')) == NULL) {
puts("Invalid input!");
exit(EXIT_FAILURE);
}
*p = '\0';
return buf;
}
int main()
{
int id = 1;
int running = 1;
struct my_struct *s;
int temp;
while (running) {
printf(" 1. add user\n");
printf(" 2. add or rename user by id\n");
printf(" 3. find user\n");
printf(" 4. delete user\n");
printf(" 5. delete all users\n");
printf(" 6. sort items by name\n");
printf(" 7. sort items by id\n");
printf(" 8. print users\n");
printf(" 9. count users\n");
printf("10. quit\n");
switch (atoi(getl("Command"))) {
case 1:
add_user(id++, getl("Name (20 char max)"));
break;
case 2:
temp = atoi(getl("ID"));
add_user(temp, getl("Name (20 char max)"));
break;
case 3:
s = find_user(atoi(getl("ID to find")));
printf("user: %s\n", s ? s->name : "unknown");
break;
case 4:
s = find_user(atoi(getl("ID to delete")));
if (s) {
delete_user(s);
} else {
printf("id unknown\n");
}
break;
case 5:
delete_all();
break;
case 6:
HASH_SORT(users, by_name);
break;
case 7:
HASH_SORT(users, by_id);
break;
case 8:
print_users();
break;
case 9:
temp = HASH_COUNT(users);
printf("there are %d users\n", temp);
break;
case 10:
running = 0;
break;
}
}
delete_all(); /* free any structures */
return 0;
}