C语言小知识——uthash使用
文章目錄
- 一、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不能有重復):
結構體中key的定義一定要有,UT_hash_handle hh也不能去掉。
hh是內部使用的hash處理句柄,在使用過程中,只需要在結構體中定義一個UT_hash_handle類型的變量即可,不需要為該句柄變量賦值,但必須在該結構體中定義該變量。
如何定義哈希表呢?
可以想到哈希表就是上面這種鍵-值對的數組,因此我們定義一個指向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, 完整用法如下:
4.插入元素 HASH_ADD_INT
由于要保持哈希表中的唯一性,在插入鍵值對之前,一定要先判斷表中是否已經存在要插入的鍵,如果已存在,就直接修改鍵對應的value;如果沒有存在,插入鍵值對。
函數使用:
參數含義:
- users:待插入的hash表;
- id:自定義的鍵值對結構體struct my_struct中,key域的變量名;即下面struct中的“id”;注意這里只把變量名輸入即可,不需要帶入值。
- 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使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AD18原理图绘制步骤
- 下一篇: dmx512协议的编程c语言,我在此分享