C :uthash
参考:
[1] uthash | 学步园
[2] 源码
[3] 官方文档
一、哈希表的概念及作用
在一般的线性表或者树中,我们所储存的值写它的存储位置的关系是随机的。因此,在查找过程中,需要一系列的与关键字的比较。算法的时间复杂度与比较的次数有关。线性表查找的时间复杂度为O(n)而平衡二叉树的查找的时间复杂度为O(log(n))。无论是采用线程表或是树进行存储,都面临面随着数据量的增大,查找速度将不同程度变慢的问题。而哈希表正好解决了这个问题。它的主要思想是通过将值与其存储位置相关联,来实现快速的随机存储。
二、uthash的基本用法
由于C语言中,并没有对hash表这类的高级数据结构进行支持,即使在目前通用的C++中,也只支持栈、队列等几个数据结构,对于map,其实是以树结构来实现的,而不是以hash表实现。
uthash是一个C语言的hash表实现。它以宏定义的方式实现hash表,不仅加快了运行的速度,而且与关键类型无关的优点。
uthash使用起来十分方便,只要将头文件uthash.h包含进去就可以使用。
目前,uthash的最新版本(1.9)支持如下平台:
- Linux
- Mac OS X
- Windows using vs2008 and vs 2010
- Solaris
- OpenBSD
通常这足够通用了。唯一的不足是在windows下,在uthash这旧版本中,并不支持vs2008和2010,而是支持。
第一步:包含头文件
#include <uthash.h>
怎么在codeblocks中使用uthash?
答:将uthash.h包复制到该文件项目下
代码实例:
#include "uthash.h"
#include <stdlib.h> /* malloc */
#include <stdio.h> /* printf */ typedef struct example_user_t {
int id;
int cookie;
UT_hash_handle hh;
} example_user_t; int main()
{
int i;
example_user_t *user, *users=NULL; /* create elements */
for(i=; i<; i++) {
user = (example_user_t*)malloc(sizeof(example_user_t));
if (user == NULL) {
exit(-);
}
user->id = i;
user->cookie = i*i;
HASH_ADD_INT(users,id,user);
} for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
printf("user %d, cookie %d\n", user->id, user->cookie);
}
return ;
}
第二步:自定义数据结构。每个结构代表一个键-值对,并且,每个结构中要有一个UT_hash_handle成员。
- id是键(key);
- name是值,即自己要保存的数据域,这里可以根据自己的需要让它变成结构体指针或者其他类型都可以;
- hh是内部使用的hash处理句柄,在使用过程中,只需要在结构体中定义一个UT_hash_handle类型的变量即可,不需要为该句柄变量赋值,但必须在该结构体中定义该变量。
- Uthash所实现的hash表中可以提供类似于双向链表的操作,可以通过结构体成员hh的
hh.prev
和hh.next获取当前节点的上一个节点或者下一个节点。
typedef struct myhash{
int id; //key
char name[]; //value
UT_hash_handle hh; //使此结构可哈希
};
第三步:定义hash表指针。这个指针为前面自定义数据结构的指针,并初始化为NULL。
struct myhash *users = NULL; //初始化为空
第四步:进行一般的操作。
增加:
- 向hashtable中添加数据
- key是int,可以使用 HASH_ADD_INT
- key是字符串,可以使用 HASH_ADD_STR
- key是指针,可以使用 HASH_ADD_PTR
- 其它,可以使用 HASH_ADD,上述实际都是调用这个方法,不过简化了参数
void hashTabel_add(HashHead *head, HashNode *users)
{
// id是key的属性名字,虽然很奇怪,实际作为宏参数会被替换掉
// 可以看下面源码,intfield会替换换成&((add)->fieldname)
if(!find_user(*head, users->id))
HASH_ADD_INT(*head, id, users);
}
查找:
- 根据key查找节点
- 如果key是int,可以使用 HASH_FIND_INT..
HashNode *find_user(HashHead head, int user_id)
{
HashNode *s;
HASH_FIND_INT(head, &user_id, s); /* s: output pointer */
return s;
}
其实现过程就是先定义一个hash结构体指针变量,然后通过HASH_FIND_INT
接口找到该key所对应的hash结构体。这里需要注意:
1、Uthash为整型key提供的查找接口为HASH_FIND_INT
;
替换:
- 与添加差不多,会在添加前,删除key相同的节点,再添加新的节点
- 如果key是int,可以使用 HASH_REPLACE_INT
void replace_user(HashHead *head, HashNode *newNode)
{
HashNode *oldNode = find_user(*head, newNode->id);
if (oldNode)
HASH_REPLACE_INT(*head, id, newNode, oldNode);
}
删除:
- 删除节点
- 使用 HASH_DEL
void delete_user(HashHead *head,HashNode *user)
{
if (user)
{
HASH_DEL(*head, user); /* user: pointer to deletee */
free(user); /* optional; it's up to you! */
}
}
只需要告诉该接口要释放哪个hash表(这里是head
)里的哪个节点(这里是user),需要注意:释放申请的hash结构体变量,uthash函数只将结构体从hash表中移除,并未释放该结构体所占据的内存。
计数:
- 统计节点数
- 使用 HASH_COUNT
int count_user(HashHead head)
{
return HASH_COUNT(head);
}
遍历:
- 遍历节点
- 可以用循环或者使用 HASH_ITER
void print_user(HashHead head)
{
HashNode *s;
printf("size is %d\n", count_user(head));
for (s = head; s != NULL; s = s->hh.next) //类似于双向链表
{
printf("user id %d, name %s\n", s->id, s->name);
}
}
void print_user_iterator(HashHead head)
{
HashNode *s, *tmp;
printf("size is %d\n", count_user(head));
HASH_ITER(hh, head, s, tmp)
{
printf("user id %d: name %s\n", s->id, s->name);
/* ... it is safe to delete and free s here */
}
}
迭代:
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
}
}
排序:
- 给节点排序,可以根据key或者value
- 使用 HASH_SORT
int name_sort(HashNode *a, HashNode *b)
{
return strcmp(a->name,b->name);
} int id_sort(HashNode *a, HashNode *b)
{
return (a->id - b->id);
} void sort_by_name(HashHead *head)
{
HASH_SORT(*head, name_sort);
} void sort_by_id(HashHead *head)
{
HASH_SORT(*head, id_sort);
}
要注意,在uthash中,并不会对其所储存的值进行移动或者复制,也不会进行内存的释放。
三、uthash的高级用法
关于键
对于uthash来说,键只是一系列的字节。所以,我们可以使用任意类型的键。包括:整形,字符串,结构等。uthash并不建议以浮点数作为键。若键的类型是结构体,那么在加入到hash表前,要对无关的数据进行清零。例如我们定义如下结构体:
typedef struct /* this structure will be our key */
{
char a;
int b;
} record_key_t;
由于系统会自动进行字节对齐,也就是在a后面加入3个点位的字节。所以,在将类型为record_key_t 的数据加入到hash表前,一定要保证该数据的无关字节的值为0,否则将可能造成存入的键值无法被查找到的情况。
下面是一段使用结构体作为键的类型的代码:
#include <stdlib.h>
#include <stdio.h>
#include "uthash.h"
typedef struct /* this structure will be our key */
{
char a;
int b;
} record_key_t;
typedef struct /* the hash is comprised of these */
{
record_key_t key;
/* ... other data ... */
UT_hash_handle hh;
} record_t;
int main(int argc, char *argv[])
{
record_t l, *p, *r, *records = NULL;
r = (record_t*)malloc( sizeof(record_t) );
memset(r, , sizeof(record_t)); /* zero fill! */
r->key.a = 'a';
r->key.b = ;
HASH_ADD(hh, records, key, sizeof(record_key_t), r);
memset(&l, , sizeof(record_t)); /* zero fill! */
l.key.a = 'a';
l.key.b = ;
HASH_FIND(hh, records, &l.key, sizeof(record_key_t), p);
if (p) printf("found %c %d/n", p->key.a, p->key.b);
return ;
}
uthash中还支持组合键,但是要求组合键的存储位置是相邻的。所谓的组合键也是利用了uthash把一切的键都看作是字节序列的原理,我们只要将组合键的第一个字段和整个组合键的长度正确的填入相关的函数就可以了。例子如下:
#include <stdlib.h> /* malloc */
#include <stddef.h> /* offsetof */
#include <stdio.h> /* printf */
#include <string.h> /* memset */
#include "uthash.h"
#define UTF32 1
typedef struct
{
UT_hash_handle hh;
int len;
char encoding; /* these two fields */
int text[]; /* comprise the key */
} msg_t;
typedef struct
{
char encoding;
int text[];
} lookup_key_t;
int main(int argc, char *argv[])
{
unsigned keylen;
msg_t *msg, *msgs = NULL;
lookup_key_t *lookup_key;
int beijing[] = {0x5317, 0x4eac}; /* UTF-32LE for 鍖椾含 */
/* allocate and initialize our structure */
msg = (msg_t*)malloc( sizeof(msg_t) + sizeof(beijing) );
memset(msg, , sizeof(msg_t)+sizeof(beijing)); /* zero fill */
msg->len = sizeof(beijing);
msg->encoding = UTF32;
memcpy(msg->text, beijing, sizeof(beijing));
/* calculate the key length including padding, using formula */
keylen = offsetof(msg_t, text) /* offset of last key field */
+ sizeof(beijing) /* size of last key field */
- offsetof(msg_t, encoding); /* offset of first key field */
/* add our structure to the hash table */
HASH_ADD( hh, msgs, encoding, keylen, msg);
/* look it up to prove that it worked :-) */
msg=NULL;
lookup_key = (lookup_key_t*)malloc(sizeof(*lookup_key) + sizeof(beijing));
memset(lookup_key, , sizeof(*lookup_key) + sizeof(beijing));
lookup_key->encoding = UTF32;
memcpy(lookup_key->text, beijing, sizeof(beijing));
HASH_FIND( hh, msgs, &lookup_key->encoding, keylen, msg );
if (msg)
printf("found /n");
free(lookup_key);
return ;
}
uthash支持将一个结构体变量存储在不同的hash表里,并且可以指定不同的字段做为键
例如:
#include <stdlib.h> /* malloc */
#include <stddef.h> /* offsetof */
#include <stdio.h> /* printf */
#include <string.h> /* memset */
#include "uthash.h" struct my_struct
{
int id; /* usual key */
char username[]; /* alternative key */
UT_hash_handle hh1; /* handle for first hash table */
UT_hash_handle hh2; /* handle for second hash table */
};
int main()
{
struct my_struct *users_by_id = NULL, *users_by_name = NULL, *s;
int i;
char *name;
s = malloc(sizeof(struct my_struct));
s->id = ;
strcpy(s->username, "thanson");
/* add the structure to both hash tables */
HASH_ADD(hh1, users_by_id, id, sizeof(int), s);
HASH_ADD(hh2, users_by_name, username, strlen(s->username), s);
/* lookup user by ID in the "users_by_id" hash table */
i=;
HASH_FIND(hh1, users_by_id, &i, sizeof(int), s);
if (s) printf("found id %d: %s/n", i, s->username);
/* lookup user by username in the "users_by_name" hash table */
name = "thanson";
HASH_FIND(hh2, users_by_name, name, strlen(name), s);
if (s)
printf("found user %s: %d/n", name, s->id);
return ;
}
注意,若要将结构体变量存储在不同的hash表里,需要在该结构体中为每个hash表定义一个UT_hash_handle字段。
选择
简单来说,select就是在一个hash表选择出一批数据,加入到另一个hash表中。它比用HASH_ADD更快。例子如下:
#include "uthash.h"
#include <stdlib.h> /* malloc */
#include <stdio.h> /* printf */
typedef struct
{
int id;
UT_hash_handle hh;
UT_hash_handle ah;
} example_user_t;
#define EVENS(x) (((x)->id & 1) == 0)
int evens(void *userv)
{
example_user_t *user = (example_user_t*)userv;
return ((user->id & ) ? : );
}
int idcmp(void *_a, void *_b)
{
example_user_t *a = (example_user_t*)_a;
example_user_t *b = (example_user_t*)_b;
return (a->id - b->id);
}
int main(int argc,char *argv[])
{
int i;
example_user_t *user, *users=NULL, *ausers=NULL;
/* create elements */
for(i=; i<; i++)
{
user = (example_user_t*)malloc(sizeof(example_user_t));
user->id = i;
HASH_ADD_INT(users,id,user);
}
for(user=users; user; user=(example_user_t*)(user->hh.next))
{
printf("user %d/n", user->id);
}
/* now select some users into ausers */
HASH_SELECT(ah,ausers,hh,users,evens);
HASH_SRT(ah,ausers,idcmp);
for(user=ausers; user; user=(example_user_t*)(user->ah.next))
{
printf("auser %d/n", user->id);
}
return ;
}
全部代码实例
#include <stdio.h>
#include <stdlib.h>
#include "uthash.h" typedef struct my_struct
{
int id; /* we'll use this field as the key */
char name[];
UT_hash_handle hh; /* makes this structure hashable */
} HashNode;
typedef HashNode* HashHead; int count_user(HashHead head);
HashNode *find_user(HashHead head, int user_id)
{
HashNode *s;
HASH_FIND_INT(head, &user_id, s); /* s: output pointer */
return s;
}
void add_user(HashHead *head, HashNode *users)
{
if(!find_user(*head, users->id))
HASH_ADD_INT(*head, id, users);
}
void replace_user(HashHead *head, HashNode *newNode)
{
HashNode *oldNode = find_user(*head, newNode->id);
if (oldNode)
HASH_REPLACE_INT(*head, id, newNode, oldNode);
}
void delete_user(HashHead *head,HashNode *user)
{
if (user)
{
HASH_DEL(*head, user); /* user: pointer to deletee */
free(user); /* optional; it's up to you! */
}
}
void print_user(HashHead head)
{
HashNode *s;
printf("size is %d\n", count_user(head));
for (s = head; s != NULL; s = s->hh.next)
{
printf("user id %d, name %s\n", s->id, s->name);
}
}
void print_user_iterator(HashHead head)
{
HashNode *s, *tmp;
printf("size is %d\n", count_user(head));
HASH_ITER(hh, head, s, tmp)
{
printf("user id %d: name %s\n", s->id, s->name);
/* ... it is safe to delete and free s here */
}
}
int count_user(HashHead head)
{
return HASH_COUNT(head);
}
int name_sort(HashNode *a, HashNode *b)
{
return strcmp(a->name,b->name);
} int id_sort(HashNode *a, HashNode *b)
{
return (a->id - b->id);
} void sort_by_name(HashHead *head)
{
HASH_SORT(*head, name_sort);
} void sort_by_id(HashHead *head)
{
HASH_SORT(*head, id_sort);
}
int main()
{
printf("--------------init---------------\n");
HashHead head = NULL;
printf("--------------add---------------\n");
HashNode *node = malloc(sizeof(HashNode));
node->id = ;
strcpy(node->name, "tom");
add_user(&head, node); node = malloc(sizeof(HashNode));
node->id = ;
strcpy(node->name, "jerry");
add_user(&head, node); node = malloc(sizeof(HashNode));
node->id = ;
strcpy(node->name, "jack");
add_user(&head, node); node = malloc(sizeof(HashNode));
node->id = ;
strcpy(node->name, "zero");
add_user(&head, node); print_user(head); printf("--------------replace---------------\n");
HashNode *newNode = malloc(sizeof(HashNode));
newNode->id = ;
strcpy(newNode->name, "rose");
replace_user(&head, newNode);
print_user(head); printf("--------------delete---------------\n");
delete_user(&head, find_user(head, ));
print_user(head);
printf("--------------sort-by-id---------------\n");
sort_by_id(&head);
print_user(head);
printf("--------------sort-by-name---------------\n");
sort_by_name(&head);
print_user(head);
return ;
}
C :uthash的更多相关文章
- 4 cocos2dx 3.0 源码分析- scheduler
scheduler 这个类, 负责了引擎的自定义更新, 及定时更新相关的操作, 看看下面的代码,很熟悉吧. schedule(schedule_selector(HelloWorld::updat ...
- java web 开发三剑客 -------电子书
Internet,人们通常称为因特网,是当今世界上覆盖面最大和应用最广泛的网络.根据英语构词法,Internet是Inter + net,Inter-作为前缀在英语中表示“在一起,交互”,由此可知In ...
- 所有selenium相关的库
通过爬虫 获取 官方文档库 如果想获取 相应的库 修改对应配置即可 代码如下 from urllib.parse import urljoin import requests from lxml im ...
- C开源hash项目uthash
uthash 是C的比较优秀的开源代码,它实现了常见的hash操作函数,例如查找.插入.删除等.该套开源代码采用宏的方式实现hash函数的相关功能,支持C语言的任意数据结构最为key值,甚至可以采用多 ...
- mysql-unsha1:在未知密码情况下,登录任意MYSQL数据库
摘要 这个POC用于在不知道明文密码的情况下对启用了密码安全认证插件(默认开启插件:mysql_native_password)的MYSQL数据库进行登录. 前提条件为: 1.为了获取到已知用户的ha ...
- Cocos2d-x 源代码分析 : Scheduler(定时器) 源代码分析
源代码版本号 3.1r,转载请注明 我也最终不out了,開始看3.x的源代码了.此时此刻的心情仅仅能是wtf! !!!!!!! !.只是也最终告别CC时代了. cocos2d-x 源代码分析文件夹 h ...
- 简单好用的hash表-----uthash
在软件开发中,不可不免的会使用到hash表,hash表的优点这里就不说了,以下介绍一个hash表的C实现, uthash是用宏实现的,使用的时候非常方便,只用包含uthash.h即可. Uthash的 ...
- c语言——uthash使用
参考:https://troydhanson.github.io/uthash/userguide.html https://blog.csdn.net/lovemust/article/detail ...
- In-Memory:内存数据库
在逝去的2016后半年,由于项目需要支持数据的快速更新和多用户的高并发负载,我试水SQL Server 2016的In-Memory OLTP,创建内存数据库实现项目的负载需求,现在项目接近尾声,系统 ...
随机推荐
- 十九种Elasticsearch字符串搜索方式终极介绍
前言 刚开始接触Elasticsearch的时候被Elasticsearch的搜索功能搞得晕头转向,每次想在Kibana里面查询某个字段的时候,查出来的结果经常不是自己想要的,然而又不知道问题出在了哪 ...
- hibernate.current_session_context_class 比较权威的解释
hibernate.current_session_context_class 博客分类: hibernate HibernateSpring多线程配置管理thread 遇到过的问题: 情景1: 在 ...
- Java团队课程设计——基于学院的搜索引擎
团队名称.团队成员介绍.任务分配,团队成员课程设计博客链接 姓名 成员介绍 任务分配 课程设计博客地址 谢晓淞(组长) 团队输出主力 爬虫功能实现,Web前端设计及其后端衔接 爬虫:https://w ...
- Java课程设计之——Web前端
主要使用的技术 Javascript/JQuery html css Jsp 前期调查 能看到的网页大致可以分为两个部分,一个是搜索的首页index.html,一个是搜索结果页/s index.htm ...
- 小小的锁,大大的疑问?Lock疑问?
Lock锁 怎么使用?怎么把下面的这个锁弄得比较合适,大家都能去买票?? 和synchronized相比的好处? lock的使用规范try finnally private final Reentra ...
- AJ学IOS 之微博项目实战(7)程序启动新特性用UICollectionViewController实现
AJ分享,必须精品 一:效果 这里实现了大多数app都会有的软件新特性的功能,用的是UICollectionViewController实现的 二:思路 这里用了UICollectionViewCon ...
- Python openpyxl使用操作和openpyxl操作
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取htt ...
- Python 控制流代码混淆简介,加大别人分析你代码逻辑和流程难度
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 王平 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自 ...
- paddlehub Test on win10
conda 构建虚拟环境 1)虚拟环境下安装paddlepaddle 1.7 2)pip install paddlehub 3)添加环境变量hub_home,以免模型把c盘撑爆 4)下载的模型在.p ...
- PHP函数:fopen
fopen() - 打开文件或者 URL. 注意:array_key_exists() 仅仅搜索第一维的键. 多维数组里嵌套的键不会被搜索到. 说明: fopen ( string $filenam ...