C语言uthash介绍
介绍
uthash是C语言比较优秀的开源代码。它实现了常见的hash函数,例如插入、查找、删除等功能。它支持C语言的任意数据类型做为key值,无论是基本数据类型还是自定义的struct,但是不同类型的key其操作接口方式略有不同,而且它甚至可以采用多个值作为key。由于该代码采用宏的方式实现,所有的实现代码都在uthash.h文件中,因此只需要在自己的代码中包含"uthash.h"头文件即可。
uthash最初是Troy D. Hanson的个人使用方便的目标,放到了SourceForge,后来因为受欢迎下载量比较大,就放到了GitHub吧。
uthash是遵从BSD开源协议,可供个人、商业和组织自由使用。
hash操作
官方文档详细介绍了uthash的使用。这里提炼一些基本信息。
https://troydhanson.github.io/uthash/userguide.html
定义hash结构体
在你定义的hash结构体中嵌入UT_hash_handle。哈希表中是通过指针操作的,不会进行移动和复制对象,因此不必担心速度和内存问题。
#include "uthash.h"
struct my_struct {
int id; /* key */
char name[10];
UT_hash_handle hh; /* makes this structure hashable */
};
key的类型
uthash支持任意类型的key,包括整型、字符串、指针、结构体等。如果key类型不同,那么add和find函数有不同的接口,但是HASH_DELETE和HASH_SORT不区分key类型。结构体的一个或者多个成员构成键
,结构体指针本身作为值
hash操作接口
macro | arguments |
---|---|
HASH_ADD_INT |
(head, keyfield_name, item_ptr) |
HASH_REPLACE_INT |
(head, keyfield_name, item_ptr, replaced_item_ptr) |
HASH_FIND_INT |
(head, key_ptr, item_ptr) |
HASH_ADD_STR |
(head, keyfield_name, item_ptr) |
HASH_REPLACE_STR |
(head, keyfield_name, item_ptr, replaced_item_ptr) |
HASH_FIND_STR |
(head, key_ptr, item_ptr) |
HASH_ADD_PTR |
(head, keyfield_name, item_ptr) |
HASH_REPLACE_PTR |
(head, keyfield_name, item_ptr, replaced_item_ptr) |
HASH_FIND_PTR |
(head, key_ptr, item_ptr) |
HASH_DEL |
(head, item_ptr) |
HASH_SORT |
(head, cmp) |
HASH_COUNT |
(head) |
举例
#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; /* must have a global pointer */
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);
}
/* 排序 */
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);
}
list操作
utlist提供了链表的操作,支持三种链表:单链表、双链表、循环双链表
。
queue
:uthash暂时没有单独提供队列的实现,可借用LL_APPEND、LL_DELETE来实现queue的功能。进一步的使用LL_INSERT_INORDER函数进行有序插入,可实现优先队列
的功能。
官方文档详细介绍了utlist的使用。在源码中包含utlist.h头文件即可。
https://troydhanson.github.io/uthash/utlist.html
定义list结构体
在你定义的list中嵌入next、prev指针,就可实现单链表或者双链表。
typedef struct element {
char *name;
struct element *prev; /* needed for a doubly-linked list only */
struct element *next; /* needed for singly- or doubly-linked lists */
} element;
list操作接口
支持list的头插法和尾插法的插入接口、查询接口、删除接口、遍历、排序等。
- 头插法:Prepend意味着在现有的列表头(如果有的话)前面插入一个元素,将列表头更改为新元素。
- 尾插法:Append意味着在列表的末尾添加一个元素,因此它成为新的尾元素。
- 连接两个链表:Concatenate接受两个正确构造的列表,并将第二个列表附加到第一个列表。
- 任意插入:要在任意元素(而不是列表头)之前添加前缀,请使用prepend ELEM宏族。要在任意元素元素之后添加元素而不是列表头,请使用append ELEM宏族。
Singly-linked | Doubly-linked | Circular, doubly-linked |
---|---|---|
LL_PREPEND(head,add); |
DL_PREPEND(head,add); |
CDL_PREPEND(head,add); |
LL_PREPEND_ELEM(head,ref,add); |
DL_PREPEND_ELEM(head,ref,add); |
CDL_PREPEND_ELEM(head,ref,add); |
LL_APPEND_ELEM(head,ref,add); |
DL_APPEND_ELEM(head,ref,add); |
CDL_APPEND_ELEM(head,ref,add); |
LL_REPLACE_ELEM(head,del,add); |
DL_REPLACE_ELEM(head,del,add); |
CDL_REPLACE_ELEM(head,del,add); |
LL_APPEND(head,add); |
DL_APPEND(head,add); |
CDL_APPEND(head,add); |
LL_INSERT_INORDER(head,add,cmp); |
DL_INSERT_INORDER(head,add,cmp); |
CDL_INSERT_INORDER(head,add,cmp); |
LL_CONCAT(head1,head2); |
DL_CONCAT(head1,head2); |
`` |
LL_DELETE(head,del); |
DL_DELETE(head,del); |
CDL_DELETE(head,del); |
LL_SORT(head,cmp); |
DL_SORT(head,cmp); |
CDL_SORT(head,cmp); |
LL_FOREACH(head,elt) {…} |
DL_FOREACH(head,elt) {…} |
CDL_FOREACH(head,elt) {…} |
LL_FOREACH_SAFE(head,elt,tmp) {…} |
DL_FOREACH_SAFE(head,elt,tmp) {…} |
CDL_FOREACH_SAFE(head,elt,tmp1,tmp2) {…} |
LL_SEARCH_SCALAR(head,elt,mbr,val); |
DL_SEARCH_SCALAR(head,elt,mbr,val); |
CDL_SEARCH_SCALAR(head,elt,mbr,val); |
LL_SEARCH(head,elt,like,cmp); |
DL_SEARCH(head,elt,like,cmp); |
CDL_SEARCH(head,elt,like,cmp); |
LL_LOWER_BOUND(head,elt,like,cmp); |
DL_LOWER_BOUND(head,elt,like,cmp); |
CDL_LOWER_BOUND(head,elt,like,cmp); |
LL_COUNT(head,elt,count); |
DL_COUNT(head,elt,count); |
CDL_COUNT(head,elt,count); |
array操作
utarray实现了动态数组的功能。可存储整型数组和字符串,也可自定义其他类型。主要功能包括:插入,查找、删除等。
queue
:可通过array实现queue功能(utarray_push_back、utarray_erase);但是没有不能实现优先队列。
https://troydhanson.github.io/uthash/utarray.html
utarray操作接口
utarray_new(UT_array *a, UT_icd *icd) |
allocate a new array |
---|---|
utarray_free(UT_array *a) |
free an allocated array |
utarray_init(UT_array *a,UT_icd *icd) |
init an array (non-alloc) |
utarray_done(UT_array *a) |
dispose of an array (non-allocd) |
utarray_reserve(UT_array *a,int n) |
ensure space available for n more elements |
utarray_push_back(UT_array *a,void *p) |
push element p onto a |
utarray_pop_back(UT_array *a) |
pop last element from a |
utarray_extend_back(UT_array *a) |
push empty element onto a |
utarray_len(UT_array *a) |
get length of a |
utarray_eltptr(UT_array *a,int j) |
get pointer of element from index |
utarray_eltidx(UT_array *a,void *e) |
get index of element from pointer |
utarray_insert(UT_array *a,void *p, int j) |
insert element p to index j |
utarray_inserta(UT_array *a,UT_array *w, int j) |
insert array w into array a at index j |
utarray_resize(UT_array *dst,int num) |
extend or shrink array to num elements |
utarray_concat(UT_array *dst,UT_array *src) |
copy src to end of dst array |
utarray_erase(UT_array *a,int pos,int len) |
remove len elements from a[pos]..a[pos+len-1] |
utarray_clear(UT_array *a) |
clear all elements from a, setting its length to zero |
utarray_sort(UT_array *a,cmpfcn *cmp) |
sort elements of a using comparison function |
utarray_find(UT_array *a,void *v, cmpfcn *cmp) |
find element v in utarray (must be sorted) |
utarray_front(UT_array *a) |
get first element of a |
utarray_next(UT_array *a,void *e) |
get element of a following e (front if e is NULL) |
utarray_prev(UT_array *a,void *e) |
get element of a before e (back if e is NULL) |
utarray_back(UT_array *a) |
get last element of a |
举例
#include <stdio.h>
#include "utarray.h"
int main() {
UT_array *nums;
int i, *p;
utarray_new(nums,&ut_int_icd);
for(i=0; i < 10; i++) utarray_push_back(nums,&i);
for(p=(int*)utarray_front(nums);
p!=NULL;
p=(int*)utarray_next(nums,p)) {
printf("%d\n",*p);
}
utarray_free(nums);
return 0;
}
string操作
utstring实现了动态字符串的功能。
https://troydhanson.github.io/uthash/utstring.html
utstring操作接口
utstring_new(s) |
allocate a new utstring |
---|---|
utstring_renew(s) |
allocate a new utstring (if s is NULL ) otherwise clears it |
utstring_free(s) |
free an allocated utstring |
utstring_init(s) |
init a utstring (non-alloc) |
utstring_done(s) |
dispose of a utstring (non-alloc) |
utstring_printf(s,fmt,…) |
printf into a utstring (appends) |
utstring_bincpy(s,bin,len) |
insert binary data of length len (appends) |
utstring_concat(dst,src) |
concatenate src utstring to end of dst utstring |
utstring_clear(s) |
clear the content of s (setting its length to 0) |
utstring_len(s) |
obtain the length of s as an unsigned integer |
utstring_body(s) |
get char* to body of s (buffer is always null-terminated) |
utstring_find(s,pos,str,len) |
forward search from pos for a substring |
utstring_findR(s,pos,str,len) |
reverse search from pos for a substring |
举例
#include <stdio.h>
#include "utstring.h"
int main() {
UT_string *s;
utstring_new(s);
utstring_printf(s, "hello world!" );
printf("%s\n", utstring_body(s));
utstring_free(s);
return 0;
}
stack操作
首先要在自己的结构体中定义next指针
https://troydhanson.github.io/uthash/utstack.html
stack操作接口
STACK_PUSH(stack,add); |
push add onto stack |
---|---|
STACK_POP(stack,elt); |
pop stack and save previous top as elt |
STACK_COUNT(stack,tmp,count); |
get number of elements into count |
STACK_TOP(stack) |
return stack |
举例
struct item {
int id;
struct item *next;
};
struct item *stack = NULL; /* 定义头指针 */
int main() {
int count;
struct item *tmp;
struct item *item = malloc(sizeof *item);
item->id = 42;
STACK_COUNT(stack, tmp, count); assert(count == 0); /* 当前栈个数为0 */
STACK_PUSH(stack, item); /* push一个元素 */
STACK_COUNT(stack, tmp, count); assert(count == 1); /* 当前栈个数为1 */
STACK_POP(stack, item); /* pop一个元素 */
free(item);
STACK_COUNT(stack, tmp, count); assert(count == 0); /* 当前栈个数为0 */
}
ringbuffer循环队列
utringbuffer实现了循环链表的相关操作。 环形缓冲区数据类型支持构造、销毁、迭代和填充
,但不支持弹出; 一旦环形缓冲区达到满容量,填充新元素会自动弹出并销毁最旧的元素。 环形缓冲区中包含的元素可以是任何简单的数据类型或结构。
ringbuffer操作接口
utringbuffer_new(UT_ringbuffer *a, int n, UT_icd *icd) |
allocate a new ringbuffer |
---|---|
utringbuffer_free(UT_ringbuffer *a) |
free an allocated ringbuffer |
utringbuffer_init(UT_ringbuffer *a, int n, UT_icd *icd) |
init a ringbuffer (non-alloc) |
utringbuffer_done(UT_ringbuffer *a) |
dispose of a ringbuffer (non-alloc) |
utringbuffer_clear(UT_ringbuffer *a) |
clear all elements from a, making it empty |
utringbuffer_push_back(UT_ringbuffer *a, element *p) |
push element p onto a |
utringbuffer_len(UT_ringbuffer *a) |
get length of a |
utringbuffer_empty(UT_ringbuffer *a) |
get whether a is empty |
utringbuffer_full(UT_ringbuffer *a) |
get whether a is full |
utringbuffer_eltptr(UT_ringbuffer *a, int j) |
get pointer of element from index |
utringbuffer_eltidx(UT_ringbuffer *a, element *e) |
get index of element from pointer |
utringbuffer_front(UT_ringbuffer *a) |
get oldest element of a |
utringbuffer_next(UT_ringbuffer *a, element *e) |
get element of a following e (front if e is NULL) |
utringbuffer_prev(UT_ringbuffer *a, element *e) |
get element of a before e (back if e is NULL) |
utringbuffer_back(UT_ringbuffer *a) |
get newest element of a |
举例
此例程创建了一个大小为7个int的循序buffer,填充10个数字,只有最后7个数字被保存下来了。
#include <stdio.h>
#include "utringbuffer.h"
int main() {
UT_ringbuffer *history;
int i, *p;
utringbuffer_new(history, 7, &ut_int_icd);
for(i=0; i < 10; i++) utringbuffer_push_back(history, &i);
for (p = (int*)utringbuffer_front(history);
p != NULL;
p = (int*)utringbuffer_next(history, p)) {
printf("%d\n", *p); /* prints "3 4 5 6 7 8 9" */
}
for (i=0; i < utringbuffer_len(history); i++) {
p = utringbuffer_eltptr(history, i);
printf("%d\n", *p); /* prints "3 4 5 6 7 8 9" */
}
utringbuffer_free(history);
return 0;
}