关于我的模块化代码的一些问题,使用void *作为C中的动态数据类型

时间:2010-03-07 04:44:24

标签: c free malloc hashtable void-pointers

几天前我发布了this question,大家都建议我使用void*,我这样做了。我认为他们中的一些人还指出了我需要照顾的一些事情,但我不确定他们究竟是什么。但是,我遇到了一些问题......

我不打算发布所有代码,因为它非常大,相反,我会发布我认为很重要的内容,希望能够帮助我。

我的哈希表结构如下:

typedef void * HashKey;
typedef void * HashValue;

typedef struct sHashItem {
    HashKey key;
    HashValue value;

    char status;
} HashItem;

typedef struct sHashTable {
    HashItem *items;

    int count;
    float load;
    int size;

    Bool (*compare)(HashKey, HashKey);
    unsigned (*hash)(void *);
} HashTable;

我的插入功能的签名如下:

Bool hashInsert(HashTable * const table, HashKey key, HashValue value);

在该函数内部,当我在哈希表中找到一个空闲桶时,我这样做:

table->items[index].key = key;
table->items[index].value = value;
table->items[index].status = USED;
table->load = ++table->count / (float)table->size;

这一切都存在一些问题:

1)正如您在上面所看到的,我只是将空闲存储桶的每个键/值对设置为与键/值hashInsert函数参数传递的相同指针。这提出了一个问题,因为您可能已经注意到了......例如,做这样的事情:

char str[50];
scanf("%s%*c", str);
hashInsert(t1, (HashKey)str, (HashValue)5);
scanf("%s%*c", str);
hashInsert(t1, (HashKey)str, (HashValue)3);

如果输入是“KeyA”然后是“KeyB”,则两者都将具有“KeyB”作为桶密钥。同样的事情适用于值而不仅仅是键,因为它们基本上是相同的类型,因为我希望我的代码完全模块化,适用于任何数据类型。

我怎么解决这个问题?

我的第一个问题是使用strdup(str)并将其传递给hashInsert函数。那样可以解决问题。由于这是在主代码中处理的,因此我可以轻松地将malloc()用于我需要传递的任何其他数据类型作为值(键可能始终是字符串或int)。

但是这个解决方案提出了另一个问题......

2)我该如何释放这个分配的内存?当然,它是由“主程序员”而不是“哈希表模块程序员”分配的,因此,“主程序员”应该在主代码中释放它,对吧?但是,这对我来说看起来不像模块化代码。

我的代码还有一个hashDestroy函数,用于释放所有已分配的内存。但是我如何使用此功能来释放一切?我不能只迭代每个键/值并在它们上使用free()因为其中一些程序员可能首先不是malloc'd并且我不需要释放它们。

如何找出hashDestroy必须免费哪些以及哪些不应该免费?

3)要完成,我想我也可以把这个问题混合起来......在第一点,我的建议是使用strdup()malloc来“修复”那个特定的问题(同时引入另一个),但对我来说也看起来不那么模块化。这个内存分配应该在哈希表模块代码中完成,而不是由“主程序员”在主代码中完成。

你怎么建议我解决这个问题?我的意思是,数据类型可以是任何东西,虽然strdup()的使用有很大帮助,但它只适用于字符串。如果我需要为某个特定结构或只是int分配内存怎么办?

对于这篇重要的帖子感到抱歉,但我认为这些问题都是相关的,我需要一些帮助来解决它们,因为我的C知识不是那么极端。我最近才了解void*所以......

5 个答案:

答案 0 :(得分:2)

答案 1 :(得分:2)

我会对所有数据进行malloc,并允许客户端在哈希表初始化时向哈希函数注册一个item_free()函数。这就是“主程序员”如何处理它。

答案 2 :(得分:1)

在哈希表中处理冲突有两种通用的解决方案:

  1. 请改用下一个免费存储桶。
  2. 存储桶存储链表,因此可以在同一存储桶中存储多个项目。
  3. 使用其中任何一个,何时释放从未出现过的问题,因为所有类型的数据都是由哈希表或哈希表的客户端分配的。如果你仍然很好奇,对这个困境的简短回答是使用smart pointers

答案 3 :(得分:1)

嗯,从我在你的例子中看到的问题不是哈希表冲突(虽然你似乎也有这个问题),它是如何管理存储在表中的项目的内存。我认为执行此类操作的标准方法是强制数据结构的用户(哈希表)执行为将要放入表中的所有内容分配空间的工作。哈希表只需要担心指针。假设您确实进行了分配,然后复制数据结构:当用户从hastable中删除项目时,用户将如何知道如何释放内存?

答案 4 :(得分:0)

要实现哈希表,我们需要一组存储桶。由于多个元素可以散列到同一个存储桶,因此每个存储桶都需要一个链表。

确实

HashItem *items;

执行上面的第二个要求?

根据你的解释,不清楚是否确实如此。

有一个很好的例子,请参阅K& R第6.6节。 link其中name = HashKey,defn = HashValue。 alt text http://www.goldfish.org/books/The%20C%20Programming%20Language%20-%20K&R/pic64.gif