uthash简单使用
在uthash中,哈希表由结构体组成,每个结构体代表一个键值关联。结构体中的一个或多个域构成键(key),结构体自身的指针就是值(value)。
- key 可以是任何数据类型。因为对于 uthash 来说,它只是一个字节序列(a sequence of bytes)。
- 哈希表中,key 必须是唯一的。意味着,在添加元素时必须检查该键是否已经存在。
- 针对不同类型的 key ,使用不同的宏来操作。
/*
* commonly used macros summary here
*/
// int-keyed hash
HASH_ADD_INT(head, intfield, add);
HASH_FIND_INT(head, findint, out);
HASH_REPLACE_INT(head, intfield, add, replaced);
// string-keyed hash (string within structure, i.e. char a[10];)
HASH_ADD_STR(head, strfield, add);
HASH_FIND_STR(head, findstr, out);
HASH_REPLACE_STR(head, strfield, add, replaced);
// string-keyed hash (structure points to string, i.e. char *str;)
HASH_ADD_KEYPTR(hh, head, keyptr, keylen, add);
// ADD, FIND same as above
// pointer-keyed hash (i.e. void *key;)
HASH_ADD_PTR(head, ptrfield, add);
HASH_FIND_PTR(head, findptr, out);
HASH_REPLACE_PTR(head, ptrfield, add, replaced);
// structure-keyed hash
HASH_ADD(hh, head, fieldname, keylen, add);
HASH_FIND(hh, head, keyptr, keylen, out);
HASH_REPLACE(hh, head, fieldname, keylen, add, replaced);
// for all type of key
HASH_COUNT(head);
HASH_ITER(hh, head, el, tmp);
HASH_DEL(head, delptr);
key为int型
定义结构体
#include "uthash.h"
typedef struct my_struct{
int id; /* key */
char name[10];
UT_hash_handle hh; /* make this structure hashalbe */
} Hash;
注意
- 结构体中必须包含 UT_hash_handle。可以任意命名。但如果命名为 hh 则可以使用便利宏(convenience macros)简化添加、查找和删除元素的参数列表。
声明哈希表
Hash *users = NULL; /* important! initialize to NULL */
重要:必须声明为指向定义结构体的空指针(NULL)。
添加
void add_user(int user_id, char *name) {
Hash *find;
HASH_FIND_INT(users, &user_id, find); /* id already in the hash? */
if (find == NULL) {
find = (Hash*)malloc(sizeof(Hash));
find->id = user_id;
HASH_ADD_INT(users, id, find); /* id: name of key field */
}
strcpy(find->name, name);
}
注意:
- 第二个参数为 key 域的名字
- 结构体一经添加到哈希表中,不要修改键。如果要实现该操作,需要先删除再添加。
传递哈希指针到函数中
上面的添加示例中,users 是一个全局变量。如果需要传递哈希表到函数中进行操作怎么办?
/* 反例 */
void add_user(Hash *users, int user_id, char *name) {
...
HASH_ADD_INT(users, id, s);
}
/* 正例 */
void add_user(Hash **users, int user_id, char *name) { ...
...
HASH_ADD_INT(*users, id, s);
}
/* 理解 */
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
理解:
- 原理同普通函数传参,如常用的 swap 交换两个变量的值。添加函数的目的是修改(增加)哈希表,只有传递指针才能达到修改的目的。
替换
HASH_REPLACE 宏等效于 HASH_ADD 宏,但 HASH_REPLACE 会尝试查找和删除待添加项目。如果找到并删除了一个项目,它还将返回该项目的指针作为输出参数。
查找
struct my_struct *find_user(int user_id) {
Hash *find;
HASH_FIND_INT(users, &user_id, find); /* find: output pointer */
return find;
}
说明
- users 是哈希表
- &user_id 指向要查找的键(key)的指针
- find 存储查找结果。如果存在,find 保存指向包含给定键值的结构体的指针,否则 find 等于 NULL。
计数
unsigned int num_users;
num_users = HASH_COUNT(users);
printf("there are %u users\n", num_users);
删除一个元素
void delete_user(Hash *del) {
HASH_DEL(users, del); /* user: pointer to deletee */
free(del); /* optional; it's up to you! */
}
说明
- users 是哈希表
- del 为指向待删除结构的指针
注意
- 删除一个元素并不会释放对应的内存,是否释放取决于用户,如上例中所示。
- 删除操作可能改变哈希表的指针(初始指向添加到哈希表中的第一个元素),比如删除了哈希表中的第一个元素
删除所有元素
void delete_all() {
Hash *cur, *tmp;
HASH_ITER(hh, users, cur, tmp) {
HASH_DEL(users, cur); /* delete; users advances to next */
free(current_user); /* optional- if you want to free */
cur = NULL;
}
}
遍历
void print_users() {
Hash *s;
for (s = users; s != NULL; s = s->hh.next) {
printf("user id %d: name %s\n", s->id, s->name);
}
}
说明
- 遍历的顺序为元素的添加顺序
- 也可以使用 hh.prev 从任意已知元素反向遍历
排序
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);
}
说明:第二个参数为排序函数,写法同 qsort 中用法。
key为字符串
键(key)为字符串型时,有以下两种情况:
- 结构体中是字符指针,例如 char* str; 使用 HASH_ADD_KEYPTR 操作
- 结构体中是字符数组,例如 char a[10]; 使用 HASH_ADD_STR 操作
字符数组型
/*
* A string-keyed hash (string within structure)
*/
#include <string.h> /* strcpy */
#include <stdlib.h> /* malloc */
#include <stdio.h> /* printf */
#include "uthash.h"
struct my_struct {
char name[10]; /* key (string is WITHIN the structure) */
int id;
UT_hash_handle hh; /* makes this structure hashable */
};
int main(int argc, char **argv)
{
const char *names[] = { "joe", "bob", "betty", NULL };
struct my_struct *s, *tmp, *users = NULL;
for (int i = 0; names[i]; ++i) {
s = (struct my_struct *)malloc(sizeof *s);
strcpy(s->name, names[i]);
s->id = i;
HASH_ADD_STR(users, name, s);
}
HASH_FIND_STR(users, "betty", s);
if (s != NULL) {
printf("betty's id is %d\n", s->id);
}
/* free the hash table contents */
HASH_ITER(hh, users, s, tmp) {
HASH_DEL(users, s);
free(s);
}
return 0;
}
字符指针型
/*
* A string-keyed hash (structure points to string)
*/
#include <string.h> /* strcpy */
#include <stdlib.h> /* malloc */
#include <stdio.h> /* printf */
#include "uthash.h"
struct my_struct {
const char *name; /* key */
int id;
UT_hash_handle hh; /* makes this structure hashable */
};
int main(int argc, char **argv)
{
const char *names[] = { "joe", "bob", "betty", NULL };
struct my_struct *s, *tmp, *users = NULL;
for (int i = 0; names[i]; ++i) {
s = (struct my_struct *)malloc(sizeof *s);
s->name = names[i];
s->id = i;
HASH_ADD_KEYPTR(hh, users, s->name, strlen(s->name), s);
}
HASH_FIND_STR(users, "betty", s);
if (s != NULL) {
printf("betty's id is %d\n", s->id);
}
/* free the hash table contents */
HASH_ITER(hh, users, s, tmp) {
HASH_DEL(users, s);
free(s);
}
return 0;
}
key为指针型
解释:这里表达的是指针自身作为 key。而不是指针指向的内容,后者使用 HASH_ADD_KEYPTR 处理。
/*
* A pointer-keyed hash (pointer itself)
*/
#include <stdio.h>
#include <stdlib.h>
#include "uthash.h"
typedef struct {
void *key;
int i;
UT_hash_handle hh;
} el_t;
el_t *hash = NULL;
char *someaddr = NULL;
int main(int argc, char **argv)
{
el_t *d;
el_t *e = (el_t*)malloc(sizeof *e);
if (e == NULL) {
return -1;
}
e->key = (void*)someaddr;
e->i = 1;
HASH_ADD_PTR(hash, key, e);
HASH_FIND_PTR(hash, &someaddr, d);
if (d != NULL) {
printf("found\n");
}
/* release memory */
HASH_DEL(hash, e);
free(e);
return 0;
}
key为结构体型
注意:
- 为了满足字节对齐,结构体中通常包含无用对齐字节(padding),因此在添加和查找元素前必须先置零(must be zeroed),使用 memset ,然后再赋对应的值,执行添加或查找。
/*
* A structure-keyed hash
*/
#include <stdlib.h>
#include <stdio.h>
#include "uthash.h"
typedef struct {
char a;
int b;
} record_key_t;
typedef struct {
record_key_t key;
/* ... other data ... */
UT_hash_handle hh;
} record_t;
int main(int argc, char *argv[]) {
record_t l, *p, *r, *tmp, *records = NULL;
r = (record_t *)malloc(sizeof *r);
memset(r, 0, sizeof *r); /* important!! */
r->key.a = 'a';
r->key.b = 1;
HASH_ADD(hh, records, key, sizeof(record_key_t), r);
memset(&l, 0, sizeof(record_t)); /* important!! */
l.key.a = 'a';
l.key.b = 1;
HASH_FIND(hh, records, &l.key, sizeof(record_key_t), p);
if (p != NULL) {
printf("found %c %d\n", p->key.a, p->key.b);
}
HASH_ITER(hh, records, p, tmp) {
HASH_DEL(records, p);
free(p);
}
return 0;
}