我正在尝试在C中调整我的开放地址哈希库的大小。在调整哈希值时,例如从大小5移动 - >大小11,我尝试创建一个新的哈希,将所有旧元素放入新的哈希,并将旧哈希指向这个新哈希。
问题在于尝试在任何调整大小期间释放这个旧哈希,在resize_OPEN
我可以释放旧的哈希键,但我实际上无法释放old_hash本身(free(old_H))而没有seg错误,所以我将永远有旧哈希的mem泄漏。对于每个调整大小,我将泄漏48个字节的散列对象。
我的put
功能和resize_OPEN
是所有调整大小发生的地方。
void put(Hash *H, int cur_key, int cur_value) {
int gen_key = 0;
if (cur_key < 0)
cur_key = cur_key * -1;
gen_key = cur_key % H->cur_size;
// Linear probing once around for a spot
k_v *new_node = createKV(cur_key, cur_value, 0);
while (1) {
// Inserting new key
if (!H->key_value[gen_key] || (H->key_value[gen_key]->k == INT_MIN)) {
H->num_elem++;
H->key_value[gen_key] = new_node;
break;
// Overwriting key
} else if (H->key_value[gen_key]->k == new_node->k) {
swap(&H->key_value[gen_key], &new_node);
free(new_node);
return;
}
// Robin hood hashing
// If the distance of the current key has probed less, swap and insert the curr key
// now that the new key is inserted
if (H->key_value[gen_key]->distance < new_node->distance) {
swap(&H->key_value[gen_key], &new_node);
gen_key = cur_key % H->cur_size;
// Don't increment distance until next check
new_node->distance--;
}
gen_key++;
new_node->distance++;
if (gen_key >= H->cur_size)
gen_key = 0;
// If we reach the probe limit, resize the hash
if (new_node->distance >= H->probe_limit) {
Hash *new_H = resize_OPEN(H);
*H = *new_H;
gen_key = new_node->k % H->cur_size;
}
}
if (H->num_elem >= H->to_resize) {
if (H->type == OPEN_ADDR) {
Hash *new_H = resize_OPEN(H);
*H = *new_H;
} else {
resize(H);
}
}
return;
}
我的调整大小功能
Hash *resize_OPEN(Hash *old_H) {
Hash *new_H = createHash(2 * old_H->cur_size);
for (int i = 0; i < old_H->cur_size; i++) {
if (old_H->key_value[i] && old_H->key_value[i]->k != INT_MIN) {
int k = old_H->key_value[i]->k;
int v = old_H->key_value[i]->v;
put(new_H, k, v);
free(old_H->key_value[i]);
}
}
free(old_H->key_value);
// How do I free the old hash here? free(old_H) will seg fault
return new_H;
}
编辑:我试图更好地解释,抱歉造成混乱。
typedef struct k_v {
int k;
int v;
int distance;
} k_v;
typedef struct Hash {
int cur_size;
int num_elem;
k_v **key_value; // Open addressing
int to_resize; // Elements to resize
int probe_limit;
double load_factor;
} Hash;
A memory leak example of when I insert 5 elements into a size 5 hash (one resize occuring)
Hash *createHash(int starting_size) {
// Get the next prime size
int index = 0;
for (; index < PRIME; index++) {
if (prime_size[index] >= starting_size) {
starting_size = prime_size[index];
break;
}
}
Hash *new_hash = (Hash *)malloc(SIZE_hash);
new_hash->key_value = (k_v **)calloc(starting_size, SIZE_kv);
new_hash->probe_limit = log_prime[index];
new_hash->cur_size = starting_size;
new_hash->num_elem = 0;
new_hash->load_factor = DEFAULT_LF; // Default load factor (change at 0.75 = N / size)
new_hash->to_resize = resize_default[index];
return new_hash;
}
答案 0 :(得分:1)
函数resize_OPEN()
中存在内存泄漏:
由于您未在最顶层调用上下文中传递H
变量的地址,resize_OPEN()
应通过复制内容来更新Hash
结构*old_H
新结构并释放新分配的结构。
但请注意,此方法相当令人不安且容易出错,并且会重新分配所有键/值节点。您可以只分配一个更大的数组并沿着新的散列大小重新调度键/值节点,并释放旧表,从而无需额外的分配和不可靠的结构副本。
未测试内存分配失败。我建议您在malloc()
和calloc()
上使用包装器来测试失败,并在失败时中止错误消息。这种malloc()
或死方法比未定义的行为更好。这样的包装器通常命名为xmalloc()
,xcalloc()
...
这是一个简单的修复:
// resize_OPEN reallocates the hash table to a larger size
// memory allocation failures are not tested.
void resize_OPEN(Hash *old_H) {
Hash *new_H = createHash(2 * old_H->cur_size);
for (int i = 0; i < old_H->cur_size; i++) {
if (old_H->key_value[i] && old_H->key_value[i]->k != INT_MIN) {
int k = old_H->key_value[i]->k;
int v = old_H->key_value[i]->v;
put(new_H, k, v);
free(old_H->key_value[i]);
}
}
free(old_H->key_value);
*old_H = *new_H;
free(new_H);
}
从put
函数
...
// If we reach the probe limit, resize the hash
if (new_node->distance >= H->probe_limit) {
resize_OPEN(H);
gen_key = new_node->k % H->cur_size;
}
}
if (H->num_elem >= H->to_resize) {
if (H->type == OPEN_ADDR) {
resize_OPEN(H);
} else {
resize(H);
}
}
...
您的代码中可能存在其他问题,您发布了最需要的信息,但没有发布可以轻松编译和测试的表单。很少有程序员可以通过单独阅读代码片段来发现复杂的问题。此外,一些问题可能隐藏在别处的定义和代码中,因此如果没有Minimal, Complete, and Verifiable Example,通常无法构建正确的诊断。