源码及分析
1、uthash.h源文件地址
2、Your structure
#include "uthash.h"
struct my_struct {
int id; /* key */
char name[10];
UT_hash_handle hh; /* makes this structure hashable */
};
哈希表是由一个一个结构体组成,结构体中有独一无二的key、用户自定义的数据、UT_hash_handle。
key可以是任意类型(int、string、ptr、struct),在插入时,必须保证key是独一无二的,可以使用HASH_FIND判断key是否被使用。结构体必须定义UT_hash_handle,一把初始化为NULL,定义hh后才能使用宏进行增删、查找等操作。
3、Hash macros
-
HASH_FIND_INT(head,findint,out)
功能:从哈希表head中查找key为findint 的项输出到out,否则out为NULL参数分析:
head:哈希表头指针,处理句柄
findint:指向key的指针
out:返回对应项的指针 -
HASH_ADD_INT(head,intfield,add)
功能:从哈希表head中添加key为intfield的数据项add
head:哈希表头指针,处理句柄
intfield:key的名字(注意:就是结构体里定义的key名字,不要奇怪,结构体中的定义当参数?其实这是一个宏,在预处理时会被替换为有效的C代码)
add:待插入项的的指针 -
HASH_REPLACE
HASH_REPLACE和HASH_ADD差不多,都是根据key改变对应的项,但是HASH_REPLACE会先使用HASH_FIND和HASH_DEL,然后再调用HASH_ADD -
HASH_DEL(head,delptr)
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 */
}
}
- HASH_COUNT
unsigned int num_users;
num_users = HASH_COUNT(users);
printf("there are %u users\n", num_users);
- Iterating and sorting
void print_users() {
struct my_struct *s;
for (s = users; s != NULL; s = s->hh.next) {
printf("user id %d: name %s\n", s->id, s->name);
}
}
4、Standard key types
key可以为任何类型:int、float、string、pointer、structure
-
int
int型key使用HASH_ADD_INT和HASH_FIND_INT宏操作,HASH_DELETE和HASH_SORT则对所有类型的key都适用 -
float
即使float型key之间的差异非常非常小,但是它们都是不同的key
使用float型key的情况几乎没有(因为uthash.h标准库里都没特别列出float型key的宏操作) -
string
如果使用string型key,关键是区分是key是char *或char [N](char *:key本身是没有在结构体内部定义,只是在结构体内部定义了一个指针,指向结构体外的key;char [N]:key本身是被定义在结构体内部,并不是指针,不懂的建议弄清楚指针和数组的区别)
char *型key使用HASH_ADD_KEYPTR
char [N]型key使用HASH_ADD_STR(其实最后都是调用HASH_ADD_KEYPTR)
查找都调用HASH_FIND_STR -
pointer
指针本身可以用作key,使用HASH_FIND_PTR、HASH_ADD_PTR
若用指针指向的值做key应使用HASH_FIND_STR、HASH_ADD_KEYPTR -
structure
对哈希表而言,使用structure型key其实相当于一串字符序列,但是在定义时,必须把key初始化为0,为什么?
struct key{
char a;
short b;
long c;
};
上面的结构体占多少字节? 7B or 8B?
正确答案是8B,但是使用的却只有其中的7B。因为为了提高CPU读数据的效率,在存储时会有字节对齐要求。虽然这样提高了读取效率,但是会浪费存储空间(以空间换时间)。以上例来说,如果在定义时不初始化key,那没利用到的那1B会被随机初始化,那么插入时基本不可能遇到key冲突,但是想要通过key查找,基本上没可能。所以必须在定义时全部初始化。
Example
#include <stdio.h> /* gets */
#include <stdlib.h> /* atoi, malloc */
#include <string.h> /* strcpy */
#include "uthash.h"
struct my_struct {
int id; /* key */
char name[10];
UT_hash_handle hh; /* makes this structure hashable */
};
struct my_struct *users = NULL;
void add_user(int user_id, 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: name of 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, *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 name_sort(struct my_struct *a, struct my_struct *b) {
return strcmp(a->name, b->name);
}
int id_sort(struct my_struct *a, struct my_struct *b) {
return (a->id - b->id);
}
void sort_by_name() {
HASH_SORT(users, name_sort);
}
void sort_by_id() {
HASH_SORT(users, id_sort);
}
int main(int argc, char *argv[]) {
char in[10];
int id = 1, running = 1;
struct my_struct *s=NULL;
unsigned num_users;
while (running) {
printf(" 1. add user\n");
printf(" 2. add/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");
gets(in);
switch(atoi(in)) {
case 1:
printf("name?\n");
add_user(id++, gets(in));
break;
case 2:
printf("id?\n");
gets(in); id = atoi(in);
printf("name?\n");
add_user(id, gets(in));
break;
case 3:
printf("id?\n");
s = find_user(atoi(gets(in)));
printf("user: %s\n", s ? s->name : "unknown");
break;
case 4:
printf("id?\n");
s = find_user(atoi(gets(in)));
if (s) delete_user(s);
else printf("id unknown\n");
break;
case 5:
delete_all();
break;
case 6:
sort_by_name();
break;
case 7:
sort_by_id();
break;
case 8:
print_users();
break;
case 9:
num_users = HASH_COUNT(users);
printf("there are %u users\n", num_users);
break;
case 10:
running = 0;
break;
}
}
delete_all(); /* free any structures */
return 0;
}
(若有不足,欢迎指教)
参考
[1]、https://blog.youkuaiyun.com/a123441/article/details/89045293
[2]、http://troydhanson.github.io/uthash/userguide.html#_the_key
[3]、http://troydhanson.github.io/uthash/