C语言哈希表uthash的使用

C语言的标准库中没有哈希表的函数可以使用,但是可以通过第三方头文件uthash.h这个包来实现哈希表的操作。首先,想要使用这个包,可以访问它的github网站 https://github.com/troydhanson/uthash 下载uthash.h文件。

使用说明

在你的程序中,首先需要定义一个结构体

1
2
3
4
5
6
7
#include "uthash.h"

struct my_struct {
int id; /* key */
char name[10];
UT_hash_handle hh; /* makes this structure hashable */
};
需要注意的是,当你添加一个结构体变量到哈希表中时,这个结构体不会被复制或者移动到其他地址,所以你还是可以在程序的生命周期中安全的指向这个结构体变量的地址。 每一个这样的结构体由一个id(被当作键值使用),一个要储存的变量,和一个UT_hash_handle变量。UT_hash_handle是非常重要的变量,它是这个结构体能够实现哈希表操作的关键。

然后需要定义一个全局的my_struct变量

1
struct my_struct *users = NULL /* important! initialize to NULL */

添加元素

1
2
3
4
5
6
7
8
9
10
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);
}

这里通过HASH_FIND_INT首先确定当前id的唯一性,然后再通过HASH_ADD_INT添加元素。如果当前元素已经存在,那么修改它储存的元素值。

查找元素

1
2
3
4
5
6
struct my_struct *find_user(int user_id) {
struct my_struct *s;

HASH_FIND_INT(users, &user_id, s); /* s: output pointer */
return s;
}

删除元素

1
2
3
4
void delete_user(struct my_struct *user) {
HASH_DEL(users, user); /* user: pointer to deletee */
free(user); /* optional; it's up to you! */
}

清空表

1
2
3
4
5
6
7
8
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 */
}
}

计算元素个数

1
2
3
unsigned int num_users;
num_users = HASH_COUNT(users);
printf("there are %u users\n", num_users);

遍历打印所有元素

1
2
3
4
5
6
7
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);
}
}

排序

  1. 通过id排序
    1
    2
    3
    4
    5
    6
    7
    int name_sort(struct my_struct *a, struct my_struct *b) {
    return strcmp(a->name, b->name);
    }

    void sort_by_name() {
    HASH_SORT(users, name_sort);
    }
  2. 通过键值排序
    1
    2
    3
    4
    5
    6
    7
    int id_sort(struct my_struct *a, struct my_struct *b) {
    return (a->id - b->id);
    }

    void sort_by_id() {
    HASH_SORT(users, id_sort);
    }

完整的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#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;
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;
}

参考资料

uthash用户使用指南 uthash简介