UTHash使用教程

UTHash使用教程

快速入门

想快速入门该模块请访问:介绍,数据接口,示例代码

介绍

Hash散列,通过关于键值(key)的函数,将数据映射到内存存储中一个位置来访问。这个过程叫做Hash,这个映射函数称做散列函数,存放记录的数组称做散列表(Hash Table),又叫哈希表

C语言中没有专门的hash处理函数,因此通常采用第三方编写的库 —— uthash

uthash是一个使用宏定义处理的C语言哈希表头文件。因此添加uthash只需要 #include “uthash.h” 即可完成高效hash操作。

一般采用hash表时,都是希望对某一组数据结构进行高效的增删改查

  • 源码github官网:https://github.com/troydhanson/uthash
  • 官方教程官网:https://troydhanson.github.io/uthash/
  • 官方参考示例:uthash-master/tests/example.c
  • 如果找不到示例代码,也可以参考这个博主摘抄的示例,也来自官方示例https://blog.csdn.net/fan_h_l/article/details/107241520

数据接口

1. 定义hash结构

假设我们要定义一个{ key : value } 为 { id : data }的哈希表,则需要按如下方式进行定义。

typedef struct example_user_t {int id;int data;UT_hash_handle hh;
} example_user_t;

2. 接口介绍INT类型的接口(宏)

uthash的接口都是宏定义,因此需要了解每个入参到底是变量还是转义标识符。

首先介绍几个概念便于后续理解:

  • 哈希结构 - 代表上文的example_user_t结构体
  • 哈希键变量 - 代表上文 int id 中的id
  • 哈希键变量的值 - 若上文 int id = 8,那么8就是这个值
  • 哈希值变量 - 代表上文的 int data
  • 哈希值变量的值 - 代表上文 data的实际值
  • 哈希头 - 通常完成一个哈希表的创建后,需要有个哈希头进行索引,每个哈希宏也都需要传入这个参数,通常成为head
  • 哈希键值对 - 表示哈希结构申明的指针变量,例如 example_user_t *user;
宏名用法含义
HASH_ADD_INTusage:[ HASH_ADD_INT(head, id, user) ]在head增加一个INT键变量id所对应的键值对结构,该结构内容为user
HASH_FIND_INTusage:[ HASH_FIND_INT(head, &user_id, user) ]在head查找INT类型键变量的值user_id所对应的键值对结构user
HASH_DELusage:[ HASH_DEL(head, user)]删除head中,键值对结构为user的键值对结构

uthash INT 示例代码:学生管理系统

1. 整体思路如下

  • 创建一个哈稀表完成 int 类型到 StudentInfo 结构体的映射。即 { int: StudentInfo };

  • 实现CRUD增删改查功能

2. 结构设计

// 既然使用hash表,首先,建立哈希表结构体[int, StudentInfo, UT_hash_handle]
// UT_hash_handle 是建立哈希索引和调用哈希宏所必须的结构体
// 再增加StudentInfo结构体,本案例的内容只有name 和 examPerform,名字和成绩,这可以根据应用场景需要进行额外增加。
// 最后建立g_Class的全局变量用于存放哈希表表头。
typedef struct StudentInfo {char name[50];int examPerform;
} StudentInfo;typedef struct peopleManger_int {int id;StudentInfo *stuInfo;UT_hash_handle hh;
} peopleManger_int;/* 全局变量作为哈稀表 */
peopleManger_int *g_Class;

3. uthash函数封装

// 因为我们哈希表的键为int型。因此用到的核心宏是
// HASH_ADD_INT   usage:[ HASH_ADD_INT(哈希表头,键变量名,当前增加哈希结构)]
// HASH_FIND_INT  usage:[ HASH_FIND_INT(哈希表头,键变量的值,用于存放当前当前增加哈希结构)]
// HASH_DEL       usage:[ HASH_DEL(哈希表头,需要删除的值变量的指针)]
// -------------------------------------------------------------------------------------
// 这里的 **item 是外部申明的值变量指针,在内部进行申请内存,因此需要采用双指针表示地址的形式。
static void __createStudent(peopleManger_int **item, int stuID)
{*item = (peopleManger_int *)malloc(sizeof(peopleManger_int));(*item)->id = stuID;HASH_ADD_INT(g_Class, id, *item);}// 这里可以看出,查找HASH_FIND_INT时候,通常会先申请一个空的哈希结构item,然后将全局哈希头中键变量ID的对应的hash结构传给item
static peopleManger_int * __findStudent(int stuID)
{peopleManger_int *item = NULL;HASH_FIND_INT(g_Class, &stuID, item);return item;
}// 哈希删除之后需要释放堆上的空间。
static void __deleteStudent(peopleManger_int *item)
{HASH_DEL(g_Class, item);free(item->stuInfo);free(item);
}

4. 增删改查

// 基于上述封装函数我们可以完成增删改查四个接口
/* 增 */
int pm_createStudent(int stuID, char *stuName, int exam)
{peopleManger_int *pmHashItem = NULL;pmHashItem = __findStudent(stuID);if (pmHashItem != NULL) {printf("has student already, please use update\n");return -1;}__createStudent(&pmHashItem, stuID);/* 学生信息的填充 */pmHashItem->stuInfo = (StudentInfo *)malloc(sizeof(StudentInfo));memcpy(pmHashItem->stuInfo->name, stuName, sizeof(char) * (strlen(stuName) + 1));pmHashItem->stuInfo->examPerform = exam;return 0;
}/* 删 */
int pm_deleteStudent(int stuID)
{peopleManger_int *pmHashItem = NULL;pmHashItem = __findStudent(stuID);if (pmHashItem == NULL) {printf("can not find item: %d\n", stuID);return -1;}__deleteStudent(pmHashItem);printf("stuID %d delete successfully\n");return 0;
}/* 改 */
int pm_updateStudent(int stuID, char *stuName, int exam)
{peopleManger_int *pmHashItem = NULL;pmHashItem = __findStudent(stuID);if (pmHashItem == NULL) {printf("dont have student already, please use create\n");return -1;}pmHashItem->id = stuID;memset(pmHashItem->stuInfo->name, 0, sizeof(char) * 50);memcpy(pmHashItem->stuInfo->name, stuName, sizeof(char) * (strlen(stuName) + 1));pmHashItem->stuInfo->examPerform = exam;return 0;
}/* 查找 */
int pm_readStudent(int stuID)
{peopleManger_int *pmHashItem = NULL;pmHashItem = __findStudent(stuID);if (pmHashItem == NULL) {printf("can not find item: %d\n", stuID);return -1;}printf("stu id: %d  | ", pmHashItem->id);printf("stu name: %s | ", pmHashItem->stuInfo->name);printf("stu exam: %d\n", pmHashItem->stuInfo->examPerform);return 0;
}

5.遍历打印所有元素

void pm_printStudentName()
{peopleManger_int *pmHashItem = NULL;for (pmHashItem = g_Class; pmHashItem != NULL; pmHashItem = (peopleManger_int *)(pmHashItem->hh.next)) {printf("stu %d, name %s, exam = %d\n", pmHashItem->id, pmHashItem->stuInfo->name, pmHashItem->stuInfo->examPerform);}
}

6. 完整代码

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "uthash.h"typedef struct StudentInfo {char name[50];int examPerform;
} StudentInfo;typedef struct peopleManger_int {int id;StudentInfo *stuInfo;UT_hash_handle hh;
} peopleManger_int;/* 全局变量作为哈稀表 */
peopleManger_int *g_Class;static void __createStudent(peopleManger_int **item, int stuID)
{*item = (peopleManger_int *)malloc(sizeof(peopleManger_int));(*item)->id = stuID;HASH_ADD_INT(g_Class, id, *item);}static peopleManger_int * __findStudent(int stuID)
{peopleManger_int *item = NULL;HASH_FIND_INT(g_Class, &stuID, item);return item;
}static void __deleteStudent(peopleManger_int *item)
{HASH_DEL(g_Class, item);free(item->stuInfo);free(item);
}/* 增 */
int pm_createStudent(int stuID, char *stuName, int exam)
{peopleManger_int *pmHashItem = NULL;pmHashItem = __findStudent(stuID);if (pmHashItem != NULL) {printf("has student already, please use update\n");return -2;}__createStudent(&pmHashItem, stuID);pmHashItem->stuInfo = (StudentInfo *)malloc(sizeof(StudentInfo));memcpy(pmHashItem->stuInfo->name, stuName, sizeof(char) * (strlen(stuName) + 1));pmHashItem->stuInfo->examPerform = exam;return 0;
}/* 删 */
int pm_deleteStudent(int stuID)
{peopleManger_int *pmHashItem = NULL;pmHashItem = __findStudent(stuID);if (pmHashItem == NULL) {printf("can not find item: %d\n", stuID);return -1;}__deleteStudent(pmHashItem);printf("stuID %d delete successfully\n");return 0;
}/* 改 */
int pm_updateStudent(int stuID, char *stuName, int exam)
{peopleManger_int *pmHashItem = NULL;pmHashItem = __findStudent(stuID);if (pmHashItem == NULL) {printf("dont have student already, please use create\n");return -2;}pmHashItem->id = stuID;memset(pmHashItem->stuInfo->name, 0, sizeof(char) * 50);memcpy(pmHashItem->stuInfo->name, stuName, sizeof(char) * (strlen(stuName) + 1));pmHashItem->stuInfo->examPerform = exam;return 0;
}/* 查找 */
int pm_readStudent(int stuID)
{peopleManger_int *pmHashItem = NULL;pmHashItem = __findStudent(stuID);if (pmHashItem == NULL) {printf("can not find item: %d\n", stuID);return -1;}printf("stu id: %d  | ", pmHashItem->id);printf("stu name: %s | ", pmHashItem->stuInfo->name);printf("stu exam: %d\n", pmHashItem->stuInfo->examPerform);return 0;
}void pm_printStudentName()
{peopleManger_int *pmHashItem = NULL;for (pmHashItem = g_Class; pmHashItem != NULL; pmHashItem = (peopleManger_int *)(pmHashItem->hh.next)) {printf("stu %d, name %s, exam = %d\n", pmHashItem->id, pmHashItem->stuInfo->name, pmHashItem->stuInfo->examPerform);}
}int main()
{int i;peopleManger_int *stu = NULL;/* create elements */printf("-----create element-----\n");for(i = 0; i < 10; i++) {char name[10] = "a_xiao";name[0] = 'a' + i;printf("here");pm_createStudent(i, name, 90 + i);}printf("-----read element-----\n");for(stu=g_Class; stu != NULL; stu=(peopleManger_int *)(stu->hh.next)) {printf("stu %d, name %s, exam = %d\n", stu->id, stu->stuInfo->name, stu->stuInfo->examPerform);}printf("delete a, go over\n");pm_deleteStudent(0);printf("-----read element-----\n");pm_printStudentName();pm_updateStudent(1, "xiaoming", 99);pm_updateStudent(0, "xiaohua", 99);pm_readStudent(1);pm_readStudent(0);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/526566.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C语言基础】gdb调试工具的使用

gdb调试教程 快速入门 要想快速入门gdb调试&#xff0c;可以直接翻阅基本案例&#xff1a;采内存 介绍 GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。 对于一名Linux下工作的c/c程序员&#xff0c;gdb是必不可少的工具 …

C/C++队列与循环队列

C/C数据结构 - 队列 循环队列 快速入门 介绍 1. 队列的定义 队列是一种线性存储结构&#xff0c;每次对队列的增删操作如下 增&#xff1a;在队列尾部添加元素 删&#xff08;取出&#xff09;&#xff1a;在队列头部删除元素 这种数据存储方式遵循“先进先出”&#xff0…

PyQt特殊对话框介绍

PyQt特殊对话框 对话框基类&#xff1a;QDialog 对话框基本用法&#xff1a; 模态窗口&#xff1a; 当窗口设置为模态窗口时&#xff0c;不能对其父级别的窗口进行操作。QTdesigner里有控件可以勾选Func: QtWidgets.Dialog.setModal(True) [ True | False ] 窗口执行 exec&am…

PyQt5 让窗口在桌面居中的办法

屏幕居中核心函数 在我们开发的UI类中定义一个center方法&#xff0c;&#xff08;方法名自定&#xff0c;内容如下&#xff09;。其中QDesktopWideget方法在Qtwidgets头文件中。 然后在 __init__()中调用该 self.center()方法即可。 # 屏幕居中def center(self):# 获取窗口大…

PyQt5 QLineEdit输入密码

单行编辑器 QLineEdit 输入密码 单行编辑器直接继承于QWidget 常用方法&#xff1a; 设置显示模式&#xff1a;在设置密码栏常会使用这种模式 方法QLineEdit.setEchoMode 用法 假设你有一个QLineEdit()组件 self.UserpswdEdit QLineEdit()则你可以使用下面三种方式定义输…

Arduino 软件开发(一): GPIO读写

Arduino 软件开发&#xff08;一&#xff09; 目录 核心运行函数&#xff0c; 基础语法&#xff0c;数字控制&#xff0c;模拟控制 核心运行函数 void setup() 当Arduino程序开始运行时会调用setup()函数。通常setup()函数完成初始化一些变量、配置引脚状态及初始化调用的库…

JavaScript学习笔记:类与导包实例

NodeJS类与导包 在工程上&#xff0c;一个文件就是一个独立的模块&#xff0c;仅包含一个类。我们可以称之为类文件。因此当我们要使用这个模块的时候&#xff0c;我们会将这个类文件作为一个包导入到调用的文件里。方法如下&#xff1a; 0. 目录结构如下 - src|__ main.js|…

JavaScript学习笔记:常量,枚举,宏定义

NodeJS常量&#xff08;fake&#xff1a;宏定义&#xff09; 因为之前C/C代码开发习惯了&#xff0c;会用到一些宏定义来替换一些表意不明的数字&#xff08;就是含义不清的数字&#xff09;。而JS没有这种宏定义。上网找到一个链接作为参考&#xff1a;https://www.itranslat…

JavaScript学习笔记:函数与箭头函数

NodeJS函数 由于js是弱语言&#xff0c;因此&#xff0c;函数的入参和出参可以为任意形式而不用指定类型。当然也没有办法使用冒号的形式进行参数类型注释。但是typescript可以使用冒号形式进行参数类型注释。 1. 普通的函数定义 不带参数形式printHello带参数形式plus使用re…

Python学习笔记(1):用户输入,终端输入

#用户输入&#xff0c;操作 print("python 用户输入操作")# input(提示字符串)&#xff0c;函数阻塞程序&#xff0c;并提醒用户输入字符串 instr input("please input the string: ") print("input >> " instr)# 将输入的字符串转化成整…

Python学习笔记(2):生成器

介绍 我们可以通过列表生成式简单直接地创建一个列表&#xff0c;但是受到内存限制&#xff0c;列表容量肯定是有限的。而且&#xff0c;创建一个包含100万个元素的列表&#xff0c;不仅占用很大的存储空间&#xff0c;而且如果我们仅仅需要访问前面几个元素&#xff0c;那后面…

Python学习笔记(3):zipfile解压库

简介 python自带zipfile解压库&#xff0c;导入方式如下 import zipfile批量解压 先尝试无密码解压&#xff0c;如果失败&#xff0c;就输入密码。但是zipfile解压大文件速度很慢。 path rL:/resource/zipPackage passwd b123456 def compressFromZip():for temp_name in…

数字万用表使用方法

数字万用表使用方法 简介&#xff1a;数字万用表相对来说&#xff0c;属于比较简单的测量仪器。本篇&#xff0c;作者就教大家数字万用表的正确使用方法。从数字万用表的电压、电阻、电流、二极管、三极管、MOS场效应管的测量等测量方法开始&#xff0c;让你更好的掌握万用表测…

Python学习笔记(4):Python如何设置类似C语言静态函数

介绍 模块封装时候&#xff0c;希望有一些函数不被外部直接调用。C语言通常采用static int function()形式以及头文件形式作为标识。而python不存在头文件&#xff0c;但可以通过修饰符staticmethod进行代替。 staticmethod def your_function():# 你的代码print("your …

c++ 字符串相等比较

介绍 在C 中比较字符串的技术 (Techniques to Compare Strings in C) Strings in C can be compared using either of the following techniques: 可以使用以下两种技术之一来比较C 中的字符串&#xff1a; String strcmp() function 字符串strcmp&#xff08;&#xff09;函…

ffmpeg下载安装 多段ts视频m3u8下载

如何下载多段ts视频 m3u8 ffmpeg ffmpeg使用教程 下载 Windows下载方法 step1. 官网下载地址 点此进入下载地址 step2. 选择 Windows EXE files 中的Windows builds by BtbN 这会跳转到github页面中。 step3. 选择对应的linux平台或者windows平台进行下载 安装 step1.…

linux环境下安装多个任意版本的python环境

linux环境下安装多个任意版本的python环境 安装方法可以归结为 [下载 编译 配置环境变量] 下载Python 参考链接&#xff1a; .任意python版本下载&#xff0c;所有python版本下载的FTP路径 Windows下载发送到linux上 step1. 下载linux版本的python 从Python官网进入 选择…

linux云服务器状态上报解决方案:外发个人邮箱

linux云服务器状态上报解决方案&#xff1a;外发个人邮箱 需求如下&#xff1a; #### 将一些服务器的关键日志实时发送到手机上一些案例&#xff1a; CPU状态检测 解决方案&#xff1a; #### 利用mailx命令外发邮件方法如下&#xff1a; 1. 用管理员打开mail配置文件 >…

Matplotlib画图教程:在QT界面中嵌入三维图片

Matplotlib画图教程&#xff1a;在QT界面中嵌入三维图片 需求&#xff1a; 做项目报告的时候&#xff0c;有这么一个想法&#xff0c;就是能通过UI随时调用matplotlib进行二维图和三维图的绘制。因此就诞生了做这么一个小模块的想法。 这里先上一下最终结果&#xff1a; 思…

Python工程笔记(1):轨迹与日志

轨迹记录模块&#xff08;Recorder&#xff09; 轨迹和日志是定位工程问题的备忘录和总结代码开发项目管理经验的基本素材&#xff0c;是工程交验收资料的重要组成部分 1.1 依赖表 包名含义安装方式系统包ostime 1.2 全局变量表 变量名含义初值log_file日志保存路径./recor…