如何解决在 C 中实现多个哈希表的最佳方法?
大家好。 我目前需要在 C 中实现 3 种不同类型的哈希表。 我试图想出一个想法,如何在不为我需要的每个表编写“创建”、“设置”、“获取”等功能的情况下以某种方式初始化这些。
表格看起来像
typedef struct ht_can_t {
uint32_t key;
list_variable *variables;
struct ht_can_t *next;
} ht_can_t;
typedef struct ht_iena_t {
uint32_t key;
list_variable *variables;
struct ht_iena_t *next;
} ht_iena_t;
typedef struct ht_variable_t {
char *key;
uint8_t length;
uint8_t *data;
struct ht_variable_t *next;
} ht_variable_t;
typedef struct {
ht_can_t **entry;
} ht_can;
typedef struct {
ht_iena_t **entry;
} ht_iena;
typedef struct {
ht_variable_t **entry;
} ht_variable;
我只会在初始化期间写这些表一次,然后只在运行时读取信息。
我当时创建、写入、添加到这个表的所有函数都会解决这个问题,因此以下只是 CAN 部分。
ht_can *ht_create_can(void) {
// Alloc space for entry
ht_can *ht = malloc(sizeof(ht_can));
// Alloc space for TABLE_SIZE times CAN Hashtable
ht->entry = malloc(sizeof(ht_can_t*) * TABLE_SIZE);
// Write NULL to every entry
for (int i=0; i < TABLE_SIZE; ++i) {
ht->entry[i] = NULL;
}
return ht;
}
ht_can_t *ht_add_can(uint32_t key,list_variable* var) {
// Alloc space for Hashtable
ht_can_t *entry = malloc(sizeof(ht_can_t));
// Write Key
entry->key = key;
// Write variable list
entry->variables = var;
// Write next as NULL
entry->next = NULL;
return entry;
}
void ht_set_can(ht_can *ht,uint32_t key,list_variable* var) {
// Get Hash value
int slot = hash_int(key);
// Access entry with Hash
ht_can_t *entry = ht->entry[slot];
// Write to if empty
if (entry == NULL) {
ht->entry[slot] = ht_add_can(key,var);
return;
}
// Search for machtching Key
while (entry != NULL) {
if (entry->key == key) {
entry->variables = var;
return;
}
entry = entry->next;
}
// if no matching key found,add to end of hashtable
entry->next = ht_add_can(key,var);
}
list_variable* ht_get_can(ht_can *ht,uint32_t key) {
// Get Hash value
int slot = hash_int(key);
// Access entry with Hash
ht_can_t *entry = ht->entry[slot];
// Return if not exist
if (entry == NULL) {
return NULL;
}
// Search for matching key
while (entry != NULL) {
if (entry->key == key) {
// Return variable List
return entry->variables;
}
entry = entry->next;
}
// Return if no matching Key exist
return NULL;
}
我是否必须为每个其他表再次编写? 或者有什么不同的方式?
解决方法
我尝试想出一个想法,如何以一种无需为我需要的每个表编写“创建”、“设置”、“获取”等函数的方式来初始化这些函数。
在 C 中实现模板的可行方法基本上是有限的。
- 使用预处理器,
- 使用单个大胖宏。不要重新发明轮子 - 对于列表,queue.h 通常可用,newlib 也有。如果没有,它只是一个要复制的标题。我还记得我还看到了一个哈希表的标题 - 当然在 gihtub 上有很多实现。
#define DECL_HASH(N,T) \
\
typedef struct {\
ht_##N##_t **entry;\
} ht_##N;\
\
typedef struct ht_##N##_t { \
uint32_t key; \
T *variables; \
struct ht_##N##_t *next; \
} ht##N##_t; \
\
ht_##N *ht_create_##N(void) { \
ht_##N *ht = malloc(sizeof(*ht)); \
ht->entry = malloc(sizeof(ht##N##_t*) * TABLE_SIZE); \
for (int i=0; i < TABLE_SIZE; ++i) { \
ht->entry[i] = NULL; \
} \
return ht; \
} \
\
/* etc. for each function to do */
DECL_HASH(can,list_variables)
struct iena_data_s { list_variables variables; };
DECL_HASH(iena,struct iena_data_s)
//etc.
- 使用您设置一些参数并包含的头文件。许多项目在不同的情况下都会这样做 - 例如 glibc strfrom*.c files。
// hash.t.h
// handy macros for concatenation
#define _c_(a,b) a##b
#define _c(a,b) _c_(a,b)
#define _c3_(a,b,c) a##b##c
#define _c3(a,c) _c3_(a,c)
// use preprocssor so that you do not have to type ( ) so much
#define ht_name_t _c3(ht_,NAME,_t)
#define ht_name _c(ht_,NAME)
typedef struct {
ht_name_t **entry;
} ht_name;
typedef struct ht_name_t { \
uint32_t key;
TYPE *variables;
struct ht_name_t *next;
} ht_name_t;
#define ht_create_name _c(ht_create_,NAME)
ht_name *ht_create_name(void) {
ht_name *ht = malloc(sizeof(*ht));
ht->entry = malloc(sizeof(ht##N##_t*) * TABLE_SIZE);
for (int i=0; i < TABLE_SIZE; ++i) {
ht->entry[i] = NULL;
}
return ht;
}
#define ht_add_name _c(ht_add_,NAME)
ht_name_t *ht_add_name(uint32_t key,list_variable* var) {
...
}
/* etc. for each function */
// pick up the trash
#undef ht_add_name
#undef ht_create_name
#undef ht_name
#undef ht_name_t
#undef _c3
#undef _c3_
#undef _c_
#undef _c
#undef NAME
// hash_can.c
#define NAME can
struct can_type_s { list_variables *variables; };
#define TYPE struct can_type_s
#include "hash.t.h"
// hash_iena.c
#define NAME iena
struct can_iena_s { list_variables *variables; };
#define TYPE struct can_iena_s
#include "hash.t.h"
- 使用实现所有操作和调度表的虚函数。 IE。 g_hash_table 作为 glib 的示例,也可能是所有 linux 内核,例如
file_operations
结构。这是您想要实现的推荐方式,它易于单元测试、易于维护并且迫使您编写漂亮的代码。一个非常非常粗略的伪代码示例,我太无聊而无法完全实现:
// hash.c
typedef void *hash_elem;
struct hash_elem_vtable_s {
// add all functions that you will need,no idea which
int (*init)(hash_elem**); // constructor
int (*copy)(hash_elem* to,const hash_elem*from); // assignemnt
int (*move)(hash_elem** to,const hash_elem*from); // move constructor
int (*destroy)(hash_elem*); // destructor
};
struct hash_s {
void *elem;
const struct hash_elem_vtable_s *ev;
struct hash_s *next;
};
int hash_add(hash_s *t,hash_elem *elem_to_add) {
...
int r = t->ev->move(&t->elem,elem_to_add);
if (!r) return -ERROR_NUMBER;
...
}
// then instantiate hash_s for each element type.
- 使用代码生成。
KamilCuk 给出了很好的答案。但是,如果您发现预处理器令人生畏,那么另一种方法是使用您喜欢的任何脚本语言,然后编写一个生成源文件的脚本。
缺点是它会使构建过程复杂化,但好处是 Python 和类似语言比 C 宏更易于阅读。
,在我看来,您应该在结构中存储维护表格所需的元素。您需要一个比较函数来比较两个条目的相等性以便能够检测冲突,您需要一个函数来计算密钥的哈希值,以及一个函数来分配密钥的副本(如果需要)(和/或也对于条目的数据元素),以防您仅存储引用或对象的完整副本。您应该在构建哈希表时传递指向这些函数的指针,并且应该在内部使用它们来管理条目。然后只需一个实现,您就可以处理所有这些类型的哈希表。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。