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;
}
posted @ 2021-08-07 13:58  zephyr~  阅读(6087)  评论(0编辑  收藏  举报