文章目录

  • 1. uthash简介
  • 2. uthash的使用
    • 2.1 定义结构体
    • 2.2 添加
    • 2.3 查找
    • 2.4 替换
    • 2.5 删除
    • 2.6 循环删除
    • 2.7 删除哈希表所有元素
    • 2.8 计算哈希表元素个数
    • 2.9 遍历哈希表中的所有项目
    • 2.10 排序哈希表
    • 2.11 完整代码
  • 3. 键值的各种类型举例
    • 3.1 整型键值
    • 3.2 字符串键值
    • 3.3 指针键值
    • 3.4 结构体键值
  • 4. 常用宏参考
    • 4.1 类型宏
    • 4.2 通用宏
    • 4.4 参数说明

1. uthash简介

  由于C语言本身不存在哈希,但是当需要使用哈希表的时候自己构建哈希会异常复杂。因此,我们可以调用开源的第三方头文件,这只是一个头文件:uthash.h。我们需要做的就是将头文件复制到您的项目中,然后:#include “uthash.h”。由于uthash仅是头文件,因此没有可链接的库代码。

  使用uthash添加,查找和删除通常是常数时间的操作,此哈希的目标是简约高效。它大约有1000行C。它会自动内联,因为它是作为宏实现的。

  uthash还包括三个额外的头文件,主要提供链表,动态数组和字符串。utlist.h为C结构提供了链接列表宏。utarray.h使用宏实现动态数组。utstring.h实现基本的动态字符串。

  github下载链接:https://github.com/troydhanson/uthash

2. uthash的使用

2.1 定义结构体

  这里我们将id作为一个索引值,也就是键值,将name作为value。

#include "uthash.h"
struct my_struct {int id;                    /* key */char name[10];UT_hash_handle hh;         /* makes this structure hashable */
};
/*声明哈希为NULL指针*/
struct my_struct *users = NULL;    /* important! initialize to NULL */

  注意:一定要包含UT_hash_handle hh;hh不需要初始化。它可以命名为任何名称,但是我们一般都命名为hh。

2.2 添加

  HASH_ADD_INT表示添加的键值为int类型

  HASH_ADD_STR表示添加的键值为字符串类型

  HASH_ADD_PTR表示添加的键值为指针类型

  HASH_ADD表示添加的键值可以是任意类型

void add_user(int user_id, char *name) {struct my_struct *s;/*重复性检查,当把两个相同key值的结构体添加到哈希表中时会报错*/HASH_FIND_INT(users, &user_id, s);  /* id already in the hash? *//*只有在哈希中不存在ID的情况下,我们才创建该项目并将其添加。否则,我们只修改已经存在的结构。*/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_ADD_INT函数中,第一个参数users是哈希表,第二个参数id是键字段的名称。最后一个参数s是指向要添加的结构的指针。

2.3 查找

struct my_struct *find_user(int user_id) {struct my_struct *s;s = (struct my_struct *)malloc(sizeof *s);HASH_FIND_INT( users, &user_id, s );  /* s: output pointer */return s;
}

  在上述代码中,第一个参数users是哈希表,第二个参数是user_id的地址一定要传递地址)。最后s是输出变量。当可以在哈希表中找到相应键值时,s返回给定键的结构,当找不到时s返回NULL。

2.4 替换

  HASH_REPLACE宏等效于HASH_ADD宏,HASH_REPLACE会尝试查找和删除项目外。如果找到并删除了一个项目,它还将返回该项目的指针作为输出参数。

void replace_user(HashHead *head, HashNode *newNode) {HashNode *oldNode = find_user(*head, newNode->id);if (oldNode)HASH_REPLACE_INT(*head, id, newNode, oldNode);
}

2.5 删除

  要从哈希表中删除结构,必须具有指向它的指针。(如果只有键,请先执行HASH_FIND以获取结构指针)。

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

  同样,这里users是哈希表,user是指向我们要从哈希中删除的结构的指针。

  删除结构只是将其从哈希表中删除,并非free 。何时释放结构的选择完全取决于自己;uthash永远不会释放您的结构

2.6 循环删除

  HASH_ITER是一个宏定义,程序执行时被替换为一个循环。

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  */}
}

2.7 删除哈希表所有元素

  如果您只想删除所有项目,但不释放它们或进行每个元素的清理,则可以通过一次操作更有效地做到这一点:

HASH_CLEAR(hh,users);

  之后,列表头(此处为users)将设置为NULL。

2.8 计算哈希表元素个数

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_16933601/article/details/107188223

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

  当users为NULL时,HASH_COUNT会返回0.

2.9 遍历哈希表中的所有项目

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);}
}

  还有一个hh.prev指针,可用于从任何已知项开始向后迭代哈希。

  由于hh.prev和hh.next字段的缘故,可以在哈希中向前和向后迭代。可以通过重复跟随这些指针来访问哈希中的所有项目,因此哈希也是双链表

2.10 排序哈希表

HASH_SORT( users, name_sort );

  第二个参数是指向比较函数的指针。它必须接受两个指针参数(要比较的项目),并且如果第一个项目分别在第二个项目之前,等于或之后排序,则必须返回小于零,零或大于零的int。 (这与标准C库中的strcmp或qsort使用的约定相同)。

int sort_function(void *a, void *b) {/* compare a to b (cast a and b appropriately)* return (int) -1 if (a < b)* return (int)  0 if (a == b)* return (int)  1 if (a > b)*/
}

  name_sort和id_sort的两个排序函数示例。

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);
}

2.11 完整代码

#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;s = (struct my_struct *)malloc(sizeof *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;
}

3. 键值的各种类型举例

3.1 整型键值

  当键值为整型时,可以使用HASH_ADD_INT和HASH_FIND_INT。(对于所有类型的键,其他操作(例如HASH_DELETE和)HASH_SORT都是相同的)。

3.2 字符串键值

  当键值为字符串时,具体要使用那个函数取决于结构体中的键值为字符串数组还是字符串指针。 这一点很重要。当结构体中的键值为字符串数组时,使用HASH_ADD_STR。键值为字符串指针时使用HASH_ADD_KEYPTR。接下来给出两个例子参考。

  当结构体中的键值为字符串数组时

#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) 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;
}

  当结构体中的键值为字符串指针时

#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) 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;
}

3.3 指针键值

#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() {el_t *d;el_t *e = (el_t *)malloc(sizeof *e);if (!e) return -1;e->key = (void*)someaddr;e->i = 1;HASH_ADD_PTR(hash,key,e);HASH_FIND_PTR(hash, &someaddr, d);if (d) printf("found\n");/* release memory */HASH_DEL(hash,e);free(e);return 0;
}

3.4 结构体键值

  在将项目添加到哈希或查找项目之前,必须将结构体键值中的元素清零。

#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);r->key.a = 'a';r->key.b = 1;HASH_ADD(hh, records, key, sizeof(record_key_t), r);memset(&l, 0, sizeof(record_t));l.key.a = 'a';l.key.b = 1;HASH_FIND(hh, records, &l.key, sizeof(record_key_t), p);if (p) 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;
}

4. 常用宏参考

4.1 类型宏

HASH_ADD_INT(head, keyfield_name, item_ptr)HASH_REPLACE_INT(head, keyfiled_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)

4.2 通用宏

HASH_ADD(hh_name, head, keyfield_name, key_len, item_ptr)HASH_ADD_BYHASHVALUE(hh_name, head, keyfield_name, key_len, hashv, item_ptr)HASH_ADD_KEYPTR(hh_name, head, key_ptr, key_len, item_ptr)HASH_ADD_KEYPTR_BYHASHVALUE(hh_name, head, key_ptr, key_len, hashv, item_ptr)HASH_ADD_INORDER(hh_name, head, keyfield_name, key_len, item_ptr, cmp)HASH_ADD_BYHASHVALUE_INORDER(hh_name, head, keyfield_name, key_len, hashv, item_ptr, cmp)HASH_ADD_KEYPTR_INORDER(hh_name, head, key_ptr, key_len, item_ptr, cmp)HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh_name, head, key_ptr, key_len, hashv, item_ptr, cmp)HASH_REPLACE(hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr)HASH_REPLACE_BYHASHVALUE(hh_name, head, keyfield_name, key_len, hashv, item_ptr, replaced_item_ptr)HASH_REPLACE_INORDER(hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr, cmp)HASH_REPLACE_BYHASHVALUE_INORDER(hh_name, head, keyfield_name, key_len, hashv, item_ptr, replaced_item_ptr, cmp)HASH_FIND(hh_name, head, key_ptr, key_len, item_ptr)HASH_FIND_BYHASHVALUE(hh_name, head, key_ptr, key_len, hashv, item_ptr)HASH_DELETE(hh_name, head, item_ptr)HASH_VALUE(key_ptr, key_len, hashv)HASH_SRT(hh_name, head, cmp)HASH_CNT(hh_name, head)HASH_CLEAR(hh_name, head)HASH_SELECT(dst_hh_name, dst_head, src_hh_name, src_head, condition)HASH_ITER(hh_name, head, item_ptr, tmp_item_ptr)HASH_OVERHEAD(hh_name, head)

4.4 参数说明

  hh_name:UT_hash_handle结构中字段的 名称。俗称 hh。

  head:结构指针变量,用作哈希的“头”。如此命名是因为它最初指向添加到哈希中的第一项。

  keyfield_name:结构中键字段的名称。(对于多字段键,这是键的第一个字段)。如果您不熟悉宏,则将字段名称作为参数传递似乎很奇怪。请参阅注释。

  key_len:键字段的长度(以字节为单位)。例如,对于整数键,它是sizeof(int),而对于字符串键,它是strlen(key)。

  key_ptr:对于HASH_FIND,这是指向要在哈希中查找的键的指针(由于它是指针,因此您不能在此处直接传递文字值)。对于 HASH_ADD_KEYPTR,这是要添加的项的键的地址。

  hashv:提供的键的哈希值。这是BYHASHVALUE宏的输入参数,是 的输出参数HASH_VALUE。如果您要重复查找相同的键,则重用缓存的哈希值可以优化性能。

  item_ptr:指向要添加,删除,替换或查找的结构的指针,或迭代期间的当前指针。这是一个输入参数HASH_ADD, HASH_DELETE和HASH_REPLACE宏,和用于输出参数HASH_FIND 和HASH_ITER。(当HASH_ITER用于迭代时,tmp_item_ptr 是与item_ptr内部使用的类型相同的另一个变量)。

  replace_item_ptr:用于HASH_REPLACE宏。这是一个输出参数,设置为指向替换的项目(如果没有替换的项目,则设置为NULL)。

  cmp:指向比较函数的指针,该函数接受两个参数(指向要比较的项目的指针),并返回一个int值,该值指定第一个项目应在第二个项目之前,等于还是之后排序(如strcmp)。

  condition:接受单个参数的函数或宏(指向结构的空指针,需要将其强制转换为适当的结构类型)。如果应“选择”结构以将其添加到目标哈希中,则函数或宏的值应为非零值。

  养成习惯,先赞后看!如果觉得写的不错,欢迎关注,点赞,在看,转发,谢谢!

C语言哈希表uthash的使用方法详解(附下载链接)相关推荐

  1. python语言格式化输出_Python format()格式化输出方法详解

    原标题:Python format()格式化输出方法详解 format() 方法的语法格式如下: str.format(args) 此方法中,str 用于指定字符串的显示样式:args 用于指定要进行 ...

  2. Jquery ajax提交表单几种方法详解

    [导读] 在jquery中ajax提交表单有post与get方式,在使用get方式时我们可以直接使用ajax 序列化表单$( 表单ID) serialize();就行了,下面我来介绍两个提交表单数据的 ...

  3. JavaScript表单序列化的方法详解

    本文介绍下,在javascript中实现表单序列化的方法,通过实例加深理解,有需要的朋友参考下吧. 在JavaScript中,可以利用表单字段的type属性,连同name和value属性一起实现对表单 ...

  4. Oracle中多表联合更新处理方法详解

    ORACLE中多表关联更新与SQL Server和MYSQL的语法稍有不同,现将ORACLE中多表关联更新的几种方法处理如下: 一.创建测试表和添加测试数据 CREATE TABLE t_test01 ...

  5. 连表查询join使用方法详解

    1.数据查询时很多时候需要连表,查询出来的内容则包含2个表中的列内容,这个时候就需要用到连表,连表又分为左连接.右连接.内连接: 2.将上图中的article和category表连表查询,查询结果中包 ...

  6. mysql ibd文件还原_MySQL单表ibd文件恢复方法详解

    前言: 随着innodb的普及,innobackup也成为了主流备份方式.物理备份对于新建slave,全库恢复的需求都能从容应对. 但当面临单表数据误删,或者单表误drop的情况,如果使用物理全备进行 ...

  7. c语言 数字分离编程,C++ 整数拆分方法详解

    一.问题背景 整数拆分,指把一个整数分解成若干个整数的和 如 3=2+1=1+1+1 共2种拆分 我们认为2+1与1+2为同一种拆分 二.定义 在整数n的拆分中,最大的拆分数为m,我们记它的方案数为 ...

  8. c语言 初始化数组,C 数组的初始化方法详解

    C 数组的初始化方法详解,数据名内涵为数组这种数据结构,在arrayTest函数体内,str是数组名,那为什么sizeof的结果却是指针的长度?这是因为: (1)数组名作为函数形参时,在函数体内,其失 ...

  9. 振动传感器的c语言程序,单片机红外对射传感器实验详解 附程序源码

    #include #define                            BUF_LENTH              128                            // ...

  10. C语言哈希表的简单实现——数组+链表(拉链法)

    C语言哈希表的简单实现--数组+链表(拉链法) 1.哈希表简介 哈希表详细介绍可以参考这篇文章 2.哈希表拉链法实现 2.1完全由本人思路实现,如有错误,欢迎批评指正 哈希声明文件hash.h /* ...

最新文章

  1. 3 css 奖品出现弹出动画_【技术】nuxt中引入wow和animate.css 页面随滚动条出现动画...
  2. 影像组学视频学习笔记(14)-特征权重做图及美化、Li‘s have a solution and plan.
  3. 每日一皮:年轻时的你,是不是也各种重构欲望?
  4. apache 编译安装php mysql_编译安装APACHE+PHP+MYSQL
  5. 剑指offer 旋转数组的最小数字
  6. nmap脚本使用总结
  7. 接口定义【领域对象】
  8. (C语言)素数是指大于1,且只能被1和它自身整除的正整数。现给定一个范围,请输出在此范围中素数的个数。
  9. 字定义JSON序列化支持datetime格式序列化
  10. springboot+druid+mybatis+mysql+多数据源
  11. java数组实现队列_使用数组在Java中进行队列实现
  12. Linux系统 nginx伪静态配置及nginx重启
  13. java day65【视图[应用] 、索引[应用] 、pl/sql 基本语法[了解] 、存储过程[理解] 、 存储函数[理解]、 触发器[理解]、Java 程序调用存储过程[应用]】...
  14. shell 第六次作业(grep正则、sed)
  15. 算法面试题_求给定字符串的排列、组合、八皇后问题
  16. 对于学习编程,你认为英语和数学哪个重要?
  17. TeamViewer安全证书过期,解决办法
  18. jFreeChart连接oracle数据库数据实现顺序图,饼形图和柱状图
  19. Servlet入门学习(二)
  20. 厦门大学,华中科技大学 再次牵手华为

热门文章

  1. 如何使用Java帮助文档
  2. 【SegMap: 3D Segment Mapping using Data-Driven Descriptors】
  3. 《算法笔记》的codeup打不开怎么办
  4. 中国光谷·“华为杯”第十九届中国研究生数学建模竞赛LaTeX模版
  5. 联想M490 BIOS H1ET69WW(1.12)刷网卡白名单
  6. 打工人福利!教你如何一秒制作日报月报
  7. 熠新科技亮相2019中国光纤传感学术会议暨产业化论坛
  8. 在win10系统上使用HiTool工具网口烧写Hi3516DV300开发板【踩坑总结:网络环境有限制】
  9. OPenDDS程序 的 实现+运行
  10. 浅谈JAVA项目开发