这种散列任何通用对象的方法是否正确?

时间:2017-03-10 02:26:31

标签: c hashcode void-pointers

使用OpenJDK hashCode,我尝试在C中实现一般的哈希例程:

U32 hashObject(void *object_generic, U32 object_length) {
    if (object_generic == NULL) return 0;

    U8 *object = (U8*)object_generic;
    U32 hash = 1;

    for (U32 i = 0; i < object_length; ++i) {
//      hash = 31 * hash + object[i]; // Original prime used in OpenJDK
        hash = 92821 * hash + object[i]; // Better constant found here: https://stackoverflow.com/questions/1835976/what-is-a-sensible-prime-for-hashcode-calculation
    }

    return hash;
}

我的想法是我可以将指针传递给任何C对象(基本类型,结构,数组等),并且该对象将被唯一地进行哈希处理。但是,由于这是我第一次做这样的事情,我想问 - 这是正确的方法吗?我是否需要注意哪些陷阱?< / p>

3 个答案:

答案 0 :(得分:3)

有明显的陷阱。例如,下面的程序使用你的函数,在gcc -O0下为每个等效对象打印一个不同的值(每次编译时都有不同的值):

#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

struct foo {
    char c;
    int i;
};

static uint32_t hashObject(void const* object_generic, uint32_t object_length) {
    if (object_generic == NULL) return 0;

    uint8_t const* object = (uint8_t const*)object_generic;
    uint32_t hash = 1;

    for (uint32_t i = 0; i < object_length; ++i) {
        hash = 92821 * hash + object[i];
    }

    return hash;
}

int main() {
    struct foo a[2];

    a[0].c = 'A';
    a[0].i = 1;

    a[1].c = 'A';
    a[1].i = 1;

    _Static_assert(
        sizeof(struct foo) == offsetof(struct foo, i) + sizeof(int),
        "struct has no end padding"
    );

    printf("%d\n", hashObject(&a[0], sizeof *a));
    printf("%d\n", hashObject(&a[1], sizeof *a));

    return EXIT_SUCCESS;
}

这是因为填充可以包含任何内容。

答案 1 :(得分:1)

在评论中,您会问如果在使用struct对象之前将其清零会发生什么。

没有用。散列可能仍然不同,因为当值存储到struct对象或struct object 1 的成员中时,填充字节采用未指定的值。未指定的值可能会在每个商店中发生变化。

还有其他类型的问题。任何标量类型(指针,整数和浮点类型)可能具有相同值的不同表示。这与结构类型具有填充字节的问题类似,如上所述。标量对象的位表示可能会更改,即使值没有,并且生成的哈希值也会不同。

(引自:ISO / IEC 9899:201x 6.2.6类型的表示6.2.6.1概述6)
当值存储在结构或联合类型的对象中时,包括在成员中 object,对象表示的字节,对应于任何填充字节 未指定的值。

答案 2 :(得分:0)

没有

std::vector<int> v1 = {1, 2, 3, 4};
std::vector<int> v2 = {1, 2, 3, 4};

std::cout << "hash1=" << hashobject(&v1, sizeof(v1)) 
    << "hash2=" << hashobject(&v1, sizeof(v1)) << std::endl;

会报告两个不同的哈希值,这可能不是预期的行为。

PS:问题是关于C而不是C ++,但类似的类可以在C中。