文章目录

  • 一、uthash是什么?
  • 二、基本hash用法
    • 1.添加头文件
    • 2.创建键-值对结构
    • 3.查找元素 HASH_FIND_INT
    • 4.插入元素 HASH_ADD_INT
    • 5.统计元素个数 HASH_COUNT
    • 6.循环表中元素 HASH_ITER
  • 总结

一、uthash是什么?

在使用python时我们常常会使用字典来进行数据检索,这个字典实际就是哈希表,通过key值来进行唯一检索。
那么,在C语言中,我们应该如何实现哈希表呢?当哈希表中元素比较简单时,我们可以直接构造一个数组,把下标看作key值,value值就是该下标对应得元素值。但是当我们需要使用很多复杂操作时,还是希望能找到一个类似于内置函数的东西去实现对于哈希表的各种操作。

于是,uthash就出现了!
uthash 是C的比较优秀的开源代码,它实现了常见的hash操作函数,例如查找、插入、删除等。该套开源代码采用宏的方式实现hash函数的相关功能,支持C语言的任意数据结构作为key值(可以是自定义的struct或基本数据类型),甚至可以采用多个值作为key,需要注意的是对于不同类型的key值,hash函数声明略有不同。(参考博文)

uthash相当于为我们提供了一个专门用于处理哈希表的函数库,在学会它里面基本函数的用法后,就可以应用在自己的程序中了。

二、基本hash用法

uthash用法详情可以参看英文使用手册。

1.添加头文件

由于uthash是以宏的方式定义了对哈希表的操作函数,因此想在代码中使用hash函数时,一定要在自己程序的初始添加uthash头文件。当然还得先下载源代码。

#include "uthash.h"

2.创建键-值对结构

uthash中定义的哈希表中每个键值对都是一个实例化的结构体,结构体定义如下:

struct my_struct {int id;                    /* key */char name[10];             /* value */UT_hash_handle hh;         /* makes this structure hashable */
};

在结构体定义中,key的数据类型可以是整型、字符、指针等,value的类型也是自定义的,并且value是不一定存在的。
我们可以在结构体中只定义key值,这样哈希表就只关注表中有没有某个key,而不关心它对应的value值;这样构造出的哈希表可以看作是python中的set,可以保证内部无重复的元素(因为哈希表的key不能有重复)

struct my_struct {int id;                    /* key */UT_hash_handle hh;         /* makes this structure hashable */
};

结构体中key的定义一定要有,UT_hash_handle hh也不能去掉。
hh是内部使用的hash处理句柄,在使用过程中,只需要在结构体中定义一个UT_hash_handle类型的变量即可,不需要为该句柄变量赋值,但必须在该结构体中定义该变量。

data = pd.read_csv('https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())

如何定义哈希表呢?

可以想到哈希表就是上面这种键-值对的数组,因此我们定义一个指向struct my_struct类型的指针就可以表示哈希表 (数组和指针的关系),可以对它进行增删改查操作。

初始化一个哈希表:

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

这里注意一定要初始化为NULL,不然会报错!


接下来介绍几个常用hash函数,全部是以key是int类型的情况来讲。

3.查找元素 HASH_FIND_INT

函数使用:

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

参数含义:

  • users:待查询的hash表;
  • &user_id:指向想查询的key的地址;user_id表示要查的key值,前面加& 取址;
  • s:表示该函数的输出值,即我们根据user_id查到的键值对;它是一个指向哈希表users中一个键值对的指针。
    因此在调用该函数前,要先定义s, 完整用法如下:
struct my_struct *find_user(int user_id) {   /* 获得key=user_id的键值对 */struct my_struct *s;   /* 定义s */HASH_FIND_INT( users, &user_id, s );  /* s: output pointer */return s;
}

4.插入元素 HASH_ADD_INT

由于要保持哈希表中的唯一性,在插入键值对之前,一定要先判断表中是否已经存在要插入的键,如果已存在,就直接修改键对应的value;如果没有存在,插入键值对。
函数使用:

HASH_ADD_INT( users, id, s );  /* id: name of key field */

参数含义:

  • users:待插入的hash表;
  • id:自定义的键值对结构体struct my_struct中,key域的变量名;即下面struct中的“id”;注意这里只把变量名输入即可,不需要带入值。
struct my_struct {int id;                    /* key */char name[10];             /* value */UT_hash_handle hh;         /* makes this structure hashable */
};
  • s:待插入的键值对结构体,是指针形式。它的key和value都要给定了。

完整用法:

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的key不存在 */s = (struct my_struct *)mallo c(sizeof *s);s->id = user_id;HASH_ADD_INT( users, id, s );  /* id: name of key field */}strcpy(s->name, name);   /* s的key存在,直接更新value值 */
}

5.统计元素个数 HASH_COUNT

函数使用:

num_users = HASH_COUNT(users);

参数含义:

  • users:待统计元素个数的hash表

函数输出即为哈希表中存在的键值对个数。

6.循环表中元素 HASH_ITER

循环哈希表的元素有两种方法

方法一:自己写for循环
在uthash中,哈希表中每个键值对之间有指针相连,并且可以通过句柄hh来实现指针调用。 每个键值对都会有一个前向指针hh.prev与后向指针hh.next,因此哈希表也可以当作双向链表使用。

我们就可以用 s = s->hh.next 实现从前向后循环的功能。

下面这个代码实现循环输出哈希表中每个键值对的功能。

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

这种方法中,在循环时s不能被随便释放或删除,因为还要获取它的next值;不过可以通过加入一个临时变量tmp,把s->hh.next 保存下来,再删除s。于是就出现了函数HASH_ITER,它里面即包含了这样一个tmp变量,使我们在循环过程中可以安全地删除s。

方法二:使用定义好的函数

循环函数HASH_ITER

struct my_struct *s, *tmp;
HASH_ITER(hh, users, s, tmp)

参数含义:

  • hh:表示hash句柄,不是个变量;
  • users:待循环的hash表;
  • s:表示每次循环时获得的那个键值对,在函数前直接定义,不用赋初值。在循环中,我们就对s进行操作。
  • tmp:就是前面提到的临时变量。这个变量从表面上来看没有什么意义,但是会在这个函数内部被使用,所以一定要声明一个tmp结构体指针(不用赋值),并送入函数。

可以用下面代码实现循环输出哈希表中每个键值对:

struct my_struct *s, *tmp;
HASH_ITER(hh, users, s, tmp) {printf("user id %d: name %s\n", s->id, s->name);/* ... it is safe to delete and free s here */
}

除了以上函数,uthash还提供了许多其他函数和高级功能,下面图中列出了全部(应该是的吧==)hash函数和它们的参数。详细用法需要参照英文使用手册。
做leetcode时有需要再去查吧hhhhh



总结

大家可以在leetcode1207上练习使用uthash。
博主也是枚新手哦,如有理解不当,欢迎大家指出~

C语言小知识——uthash使用相关推荐

  1. 纸上得来终觉浅(c语言小知识总结)

    纸上得来终觉浅(c语言小知识总结) 1.数组的初始定义 对于一个初始定义的数组,内部的值是随机的,若用{}(哪怕其中没有元素)也会让数组内元素初始化,默认为0. 若是用循环语句进行赋值,在一个长度为2 ...

  2. eem二级c语言题库哪种比较好,c语言小知识,供初学者参考

    1 用预处理指令 define 声明一个常数 用以表明 1 年中有多少秒 忽略闰年问题 define SECONDS PER YEAR 60 60 24 365 UL 2 写一个 标准 宏 MIN 这 ...

  3. C语言小知识---递归函数的使用

      C语言允许函数自身调用自身,这种调用就被称为递归.好多人刚开始学习递归的时候,往往被一层层嵌套调用搞糊涂了,搞不清楚到底是怎么调用的?现在就通过一个小例子来演示一下,递归调用时,函数是如何运行的. ...

  4. C语言小知识:typedef\函数模板\

    (1)typedef用法: typedef为C语言的关键字,作用是为一种数据类型定义一个新名字.这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等).在编程中使用t ...

  5. C语言小知识---为什么要使用指针

      刚开始学习C语言的时候,感觉最难理解的就是指针,什么指针变量,变量指针,指向指针的变量,指向变量的指针?一堆概念,搞得人云里雾里的,今天不讨论这些概念的问题,从最底层来分析C语言中为什么要使用指针 ...

  6. C语言小知识---特殊的字符串

      字符串在程序中很常见,和整数.小数.字符其他数据类型一样,是代码中必不可少的一部分.可是字符串却又和其他类型不完全一样,还有自己的个性.   下面直接通过一段代码来看. 定义了一个字符1.一个字符 ...

  7. C语言小知识---奇葩的小数

      看到这个标题,好多人可能会想,小数有什么奇葩的,这不和整数一样,加减乘除计算起来也没啥区别呀?奇葩在哪里?   下面通过一个简单的例子来看一看,定义一个小数一个整数,然后打印出来. 核心代码其实只 ...

  8. C语言小知识---printf()函数

      说起printf()函数,写代码的同学肯定都很熟悉,这是C语言中标准的打印函数,在调试代码或者信息输出的时候会经常用到. 其中printf函数的转换说明如下: 转换说明修饰符 printf()中的 ...

  9. C语言小知识---数据类型

      看到这个题目好多人肯定会迷惑,数据类型有什么可说的,这不是编程的基础吗?凡是会写代码的肯定都熟悉数据类型,不就是char,int,float,double这些每天都用成百上千次的的类型吗?各位看官 ...

  10. C语言小知识---printf()函数转换符的意义

      printf()函数大家已经很熟悉了,它的转换符在打印数据的时候也会经常使用,比如%c,%d,%f等.那么为什么打印的时候一定需要转换符呢?系统难道不能自动识别吗?转换符存在的意义又是什么?    ...

最新文章

  1. Spring处理器(Controller)全局建言
  2. JVM方法区内存分配
  3. 【软件工程】技术规格说明书
  4. 如何把SAP Kyma和SAP Cloud for Customer连接起来
  5. python学习-列表解析、字典解析
  6. Educational Codeforces Round 117 (Rated for Div. 2)
  7. POLLERR的故事
  8. Mint-ui中loadmore(上拉加载下拉刷新)组件在ios中滑动会触发点击事件的解决方法...
  9. java调用jndi出错,Webshpere数据源错误:无法查找JNDI名称
  10. python数组写入txt
  11. 洛谷P1141 01迷宫【bfs】
  12. 《构架师的12项修炼》读书笔记
  13. 线下沙龙:靠谱的区块链应用到底是啥样?
  14. 用php照片艺术化,Lab:照片艺术化调色处理介绍
  15. php中$this-是什么意思
  16. 5.雅思口语——别再用delicious food啦
  17. C++第33课--C++中的字符串类
  18. 用python做线性规划
  19. 三氟磺隆(CAS 82097-50-5)的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  20. 用python表白代码_python浪漫表白源码

热门文章

  1. Vivado2018使用教程
  2. stata15无法安装spost13命令包,报错:stacktrace not available如何解决?
  3. Spring源码下载地址
  4. 大漠插件ocr多选字库_大漠ocr识别字库的生成,和使用方法
  5. Java word转pdf方法
  6. java飞机大战boos代码_飞机大战 java 源代码
  7. 给intellij IDEA设置背景颜色
  8. 在线类图各种UML图分享
  9. GOF设计模式(概念、原则、场景、优点、缺点、应用)
  10. 永宏plc和台达vfd-m变頻器modbs rtu通讯程序史上最好用的plc和变頻器rtu通讯程序