C語言uthash的用法

文章目錄

  • 1 定義一個哈希表
    • UT_hash_handle
  • 2 哈希操作
    • 聲明
    • 添加
    • 查找
    • 刪除
    • 獲取哈希表中元素個數
    • 迭代
    • 排序
  • 3 案例
  • 鍵的使用

官網解釋:https://troydhanson.github.io/uthash/userguide.html
在使用之前,我們必須包含uthash.h的頭文件,你需要將該頭文件加入到你的項目中

#include "uthash.h"

1 定義一個哈希表

我們直到,在哈希表中,最重要的就是鍵和值,在 utash 中,哈希表由結構體組成。 每個結構體代表一個鍵值關聯。 一個或多個結構體里的域構成鍵, 該結構指針就是值。

我們定義一個my_struct 結構體,用id的值作為鍵值,my_struct 的實例指針作為值

#include "uthash.h"struct my_struct {int id;                    /* key */char name[10];UT_hash_handle hh;         /* makes this structure hashable */
};

鍵的數據類型或名稱沒有限制,可以具有任何名稱和數據類型。但是再把值添加到哈希表之前,必須保證鍵沒有被使用,保證鍵的唯一性。可以使用HASH_FIND檢查哈希表是否已存在該鍵。

值就是結構體實例的指針

UT_hash_handle

UT_hash_handle 字段必須要存在于你定義的結構體中,它不需要初始化,可以命名任何東西,比如hh,UT_hash_handle 用來標記該結構體是哈希表。

2 哈希操作

聲明

我們必須將聲明的哈希表結構體初始化為NULL指針

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

添加

按照合適非方法分配和初始化結構體,初始化必須保證鍵是唯一值,然后調用HASH_ADD添加到哈希表中,這里我們使用便利宏 HASH_ADD_INT,它為 int 類型的鍵提供了簡化的用法。

void add_user(int user_id, char *name) {struct my_struct *s;s = malloc(sizeof *s);s->id = user_id;strcpy(s->name, name);HASH_ADD_INT(users, id, s);  /* id: name of key field */
}

HASH_ADD_INT 的第一個參數是哈希表,第二個參數是鍵的名稱。,在這里,這是 id。 最后一個參數是指向要添加的結構指針。

將結構添加到哈希表后,不要更改其鍵的值。 我們要從哈希中刪除該項目,更改鍵值,然后重新添加它。

在上面的示例中,我們沒有檢查 user_id 是否已經是哈希中某個現有項目的鍵值。 如果你的程序有可能生成重復鍵,則必須在將鍵添加到散哈希表之前顯式檢查唯一性。 如果鍵已經在散列中,可以簡單地修改散列中的現有結構,而不是添加項。 將具有相同鍵的兩個項目添加到哈希表是錯誤的

讓我們重寫 add_user 函數來檢查 id 是否在哈希中。 只有當 id 不存在于散列中時,我們才創建項目并添加它。 否則我們只是修改已經存在的結構。

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

查找

要在哈希中查找結構體,你需要它的鍵。 然后調用 HASH_FIND。 (這里我們使用便利宏 HASH_FIND_INT 來處理 int 類型的鍵)。

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

這里,哈希表是users,&user_id 指向鍵(本例中為整數)。 最后,s 是 HASH_FIND_INT 的輸出變量。 最終結果是 s 指向具有給定鍵的結構,或者如果在散列中找不到鍵,則為 NULL。

刪除

要從哈希中刪除結構體,你必須有一個指向它的指針。 (如果你只有鍵,首先做一個 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 是指向我們要從哈希中刪除的結構的指針。

刪除所有元素:HASH_ITER 宏是一個刪除安全的迭代構造,它擴展為一個簡單的 for 循環。

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

獲取哈希表中元素個數

可以使用 HASH_COUNT 獲取哈希表中的結構體數:

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

迭代

你可以通過從頭開始并跟隨 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);}
}

排序

當你跟隨 hh.next 指針時,以“插入順序”訪問散列中的項目。 你可以使用 HASH_SORT 將項目排序為新順序。

HASH_SORT(users, cmpfcn);

第二個參數是一個指向比較函數的指針。 它必須接受兩個指針參數(要比較的項目),并且必須返回一個小于零、零或大于零的 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)*/
}
int by_name(const struct my_struct *a, const struct my_struct *b) {return strcmp(a->name, b->name);
}int by_id(const struct my_struct *a, const struct my_struct *b) {return (a->id - b->id);
}void sort_by_name() {HASH_SORT(users, by_name);
}void sort_by_id() {HASH_SORT(users, by_id);
}

3 案例

#include <stdio.h>   /* printf */
#include <stdlib.h>  /* atoi, malloc */
#include <string.h>  /* strcpy */
#include "uthash.h"struct my_struct {int id;                    /* key */char name[21];UT_hash_handle hh;         /* makes this structure hashable */
};struct my_struct *users = NULL;void add_user(int user_id, const 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 is the key field */}strcpy(s->name, name);
}struct my_struct *find_user(int user_id)
{struct my_struct *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;struct my_struct *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 by_name(const struct my_struct *a, const struct my_struct *b)
{return strcmp(a->name, b->name);
}int by_id(const struct my_struct *a, const struct my_struct *b)
{return (a->id - b->id);
}const char *getl(const char *prompt)
{static char buf[21];char *p;printf("%s? ", prompt); fflush(stdout);p = fgets(buf, sizeof(buf), stdin);if (p == NULL || (p = strchr(buf, '\n')) == NULL) {puts("Invalid input!");exit(EXIT_FAILURE);}*p = '\0';return buf;
}int main()
{int id = 1;int running = 1;struct my_struct *s;int temp;while (running) {printf(" 1. add user\n");printf(" 2. add or 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");switch (atoi(getl("Command"))) {case 1:add_user(id++, getl("Name (20 char max)"));break;case 2:temp = atoi(getl("ID"));add_user(temp, getl("Name (20 char max)"));break;case 3:s = find_user(atoi(getl("ID to find")));printf("user: %s\n", s ? s->name : "unknown");break;case 4:s = find_user(atoi(getl("ID to delete")));if (s) {delete_user(s);} else {printf("id unknown\n");}break;case 5:delete_all();break;case 6:HASH_SORT(users, by_name);break;case 7:HASH_SORT(users, by_id);break;case 8:print_users();break;case 9:temp = HASH_COUNT(users);printf("there are %d users\n", temp);break;case 10:running = 0;break;}}delete_all();  /* free any structures */return 0;
}

鍵的使用

  1. 如果是字符串當鍵,有兩種,一種是char * ,使用XXX_XXX_KETPTR,兩一種是char [] ,比如char name[10],使用XXX_XXX_STR
  2. 如果指針自己是鍵,使用XXX_XXX_PTR,如果指針指向的是鍵,比如char *,char *name = “aaa”,aaa作為鍵,則使用XXX_XXX_KEYPTR
  3. 如果是整數,使用XXX_XXX_INT
  4. 鍵可是是任何數據類型,任何數據類型可以使用通用宏HASH_ADD和HASH_FIND

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379534.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379534.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379534.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Javascript Paste Keyboard Shortcuts Hijack

author : kj021320 team : I.S.T.O 這樣的攻擊手段也算是極其無恥 猥瑣之極! 所以防御措施一定要做好 首先說一下通過Javascript Paste Keyboard Shortcuts Hijack能做什么???能夠讀取你本地機器任何文件! 沒錯!也就是說 你中了任何一個XSS 加上你按了粘貼快捷鍵后,你就有可…

python 生成器表達式_Python中的列表理解與生成器表達式

python 生成器表達式The list is a collection of different types of elements and there are many ways of creating a list in Python. 該列表是不同類型元素的集合&#xff0c;并且有許多方法可以在Python中創建列表。 清單理解 (List Comprehension) List comprehension…

Javaweb---監聽器

1.什么是監聽器 監聽器就是監聽某個對象的狀態變化的組件。 事件源&#xff1a;被監聽的對象 ----- 三個域對象 request session servletContext 監聽器&#xff1a;監聽事件源對象 事件源對象的狀態的變化都會觸發監聽器 ---- 62 注冊監聽器&#xff1a;將監聽器與事件源進行…

Linux中的Ramdisk和Initrd

Ramdisk簡介先簡單介紹一下ramdisk&#xff0c;Ramdisk是虛擬于RAM中的盤(Disk)。對于用戶來說&#xff0c;能把RAM disk和通常的硬盤分區&#xff08;如/dev/hda1&#xff09;同等對待來使用&#xff0c;例如&#xff1a;redice # mkfs.ext2 /dev/ram0mke2fs 1.38 (30-Jun-200…

slab下kmalloc內核函數實現

文章目錄kmalloc的整體實現獲取高速緩存高速緩存獲取index總結https://blog.csdn.net/qq_41683305/article/details/124554490&#xff0c;在這篇文章中&#xff0c;我們介紹了伙伴算法、slab機制和常見的內存管理函數&#xff0c;接下來&#xff0c;我們看看kmalloc內核函數的…

PHP array_merge_recursive()函數與示例

PHP array_merge_recursive()函數 (PHP array_merge_recursive() function) array_merge_recursive() function is used to merge two or more arrays, it returns a new array with merged elements. The only difference between array_merge() and array_merge_recursive() …

標題:三羊獻瑞

標題&#xff1a;觀察下面的加法算式&#xff1a; 其中&#xff0c;相同的漢字代表相同的數字&#xff0c;不同的漢字代表不同的數字。 請你填寫“三羊獻瑞”所代表的4位數字&#xff08;答案唯一&#xff09;&#xff0c;不要填寫任何多余內容。 思路分析&#xff1a; 首先…

hdu 1069

地址&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1069 題意&#xff1a;給定若干個木塊長寬高&#xff0c;長寬高可以自己調整&#xff0c;求堆積起來最高的高度。 mark&#xff1a;枚舉所有木塊長寬高可能情況&#xff0c;簡單dp。 代碼&#xff1a; #include <…

簡明 Python 編程規范

簡明 Python 編程規范編碼 所有的 Python 腳本文件都應在文件頭標上 # -*- coding:utf-8 -*- 。設置編輯器&#xff0c;默認保存為 utf-8 格式。注釋 業界普遍認同 Python 的注釋分為兩種的概念&#xff0c;一種是由 # 開頭的“真正的”注釋&#xff0c;另一種是 docstri…

進程虛擬地址管理

文章目錄1 地址分布實際使用中的內存區域2 進程的虛擬地址描述用戶空間mmap線程之間共享內存地址的實現機制1 地址分布 現在采用虛擬內存的操作系統通常都使用平坦地址空間&#xff0c;平坦地址空間是指地址空間范圍是一個獨立的連續空間&#xff08;比如&#xff0c;地址從0擴…

java兩個文件夾比較路徑_比較Java中兩個文件的路徑

java兩個文件夾比較路徑Given the paths of the two files and we have two compare the paths of the files in Java. 給定兩個文件的路徑&#xff0c;我們有兩個比較Java中文件的路徑。 Comparing paths of two files 比較兩個文件的路徑 To compare the paths of two file…

標題:加法變乘法

標題&#xff1a;我們都知道&#xff1a;123 … 49 1225 現在要求你把其中兩個不相鄰的加號變成乘號&#xff0c;使得結果為2015 比如&#xff1a; 123…10*1112…27*2829…49 2015 就是符合要求的答案。 請你尋找另外一個可能的答案&#xff0c;并把位置靠前的那個乘號左…

C# winform對話框用法大全收藏

對話框中我們常用了以下幾種&#xff1a; 1、文件對話框(FileDialog) 它又常用到兩個&#xff1a; 打開文件對話框(OpenFileDialog) 保存文件對話(SaveFileDialog) 2、字體對話框(FontDialog) 3、顏色對話框(&#xff23;olorDialog) 4、打印預瀏對話框(PrintPreviewDialog) 5、…

【翻譯】eXpressAppFramework QuickStart 業務模型設計(四)—— 實現自定義業務類...

這一講&#xff0c;你將學到如何從頭開始實現業務類。為此&#xff0c;將要實現Department和Position業務類。這些類將被應用到之前實現的Contact類中。你將學到引用對象自動生成用戶界面的基本要素。 在此之前&#xff0c;我建議你去閱讀一下 【翻譯】eXpressAppFramework Qui…

內存重映射

文章目錄1 kmap2 映射內核內存到用戶空間使用remap_pfn_range使用io_remap_pfn_rangemmap文件操作建立VMA和實際物理地址的映射mmap 之前分配 一次性映射mmap 之前分配 Page FaultPage Fault 中分配 映射內核內存有時需要重新映射&#xff0c;無論是從內核到用戶空間還是從內…

math.sqrt 有問題_JavaScript中帶有示例的Math.sqrt()方法

math.sqrt 有問題JavaScript | Math.sqrt()方法 (JavaScript | Math.sqrt() Method) The Math.sqrt() method is inbuilt in JavaScript to find the square root of a number. In this tutorial, we will learn about the sqrt() method with examples. JavaScript中內置了Mat…

標題:移動距離

標題&#xff1a;移動距離 X星球居民小區的樓房全是一樣的&#xff0c;并且按矩陣樣式排列。其樓房的編號為1,2,3… 當排滿一行時&#xff0c;從下一行相鄰的樓往反方向排號。 比如&#xff1a;當小區排號寬度為6時&#xff0c;開始情形如下&#xff1a; 1 2 3 4 5 6 12 11 1…

ISAPI Rewrite 實現簡單url重寫、二級域名重寫

實現步驟&#xff1a; 第一步&#xff1a;下載ISAPI_Rewrite.rar&#xff0c;將Rewrite文件夾和httpd.ini直接放在項目根目錄下面。 第二步&#xff1a;IIS配置&#xff0c;篩選Rewrite文件夾里面的Rewrite.dll文件&#xff0c;如圖&#xff1a; 第三步&#xff1a;在httpd.ini…

用戶登錄

用戶登錄 代碼namespace 用戶登錄 {public partial class Form1 : Form{public Form1(){InitializeComponent();}bool b1, b2, b3, b4, b5, b6;private void button1_Click(object sender, EventArgs e){try{if (b1 && b2 && b3 && b4 && b5 &…

進程上下文和中斷上下文

文章目錄進程的preempt_count變量thread_infopreempt_counthardirq相關softirq相關上下文原文鏈接&#xff1a; https://zhuanlan.zhihu.com/p/88883239進程的preempt_count變量 thread_info 在內核中&#xff0c;上下文的設置和判斷接口可以參考 include/linux/preempt.h 文…