内存泄漏调整哈希C的大小

时间:2017-08-19 13:44:29

标签: c pointers hash memory-leaks

我正在尝试在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;
}

1 个答案:

答案 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,通常无法构建正确的诊断。