使用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>
答案 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中。