微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 C 中实现多个哈希表的最佳方法?

如何解决在 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 中实现模板的可行方法基本上是有限的。

  1. 使用预处理器,
    • 使用单个大胖宏。不要重新发明轮子 - 对于列表,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"
  1. 使用实现所有操作和调度表的虚函数。 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.
  1. 使用代码生成。
,

KamilCuk 给出了很好的答案。但是,如果您发现预处理器令人生畏,那么另一种方法是使用您喜欢的任何脚本语言,然后编写一个生成源文件的脚本。

缺点是它会使构建过程复杂化,但好处是 Python 和类似语言比 C 宏更易于阅读。

,

在我看来,您应该在结构中存储维护表格所需的元素。您需要一个比较函数来比较两个条目的相等性以便能够检测冲突,您需要一个函数来计算密钥的哈希值,以及一个函数来分配密钥的副本(如果需要)(和/或也对于条目的数据元素),以防您仅存储引用或对象的完整副本。您应该在构建哈希表时传递指向这些函数的指针,并且应该在内部使用它们来管理条目。然后只需一个实现,您就可以处理所有这些类型的哈希表。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。