散列函数问题 - C.

时间:2010-10-23 09:40:00

标签: c hash

我正在使用K& R书中提供的以下散列函数。

#define HASHSIZE 101
unsigned hash(char *s)
{
    unsigned hashval;
    for (hashval = 0; *s != '\0'; s++)
        hashval = *s + 31 * hashval;
    return hashval % HASHSIZE;
}

在我的项目中,我打开了更多警告(警告也被视为错误),上述代码将无法编译。

error: conversion to ‘unsigned int’ from ‘char’ may change the sign of the result

如果我对hashval签名,我会得到负哈希值。我想知道如何解决这个问题。

任何帮助?

3 个答案:

答案 0 :(得分:4)

您的编译器正在接受并警告您,您隐式更改了对s指向的区域中存储的字节的解释。函数原型指定s为指向char的指针,默认情况下,您的设置char似乎已签名。但是,要使算术运算正确,您只需要使用无符号值。所以问题是:编译器应该通过实际具有负值的s指向的值做什么?

让我们快速转移,以确保我们了解我们可能正在考虑的价值观。 signed char的可能值为CHAR_MINCHAR_MAX。 (这些值可以在limits.h中找到。)unsigned char的可能值为0UCHAR_MAX。所以问题就变成了这样:我们如何在CHAR_MINCHAR_MAX范围内表示从0UCHAR_MAX的可能值范围?

一个简单的方法就是让编译器为您执行此转换:它只是使用环绕算法来确保该值在限制范围内:它会自动添加UCHAR_MAX + 1足够的时间来获取一个值在0UCHAR_MAX范围内。 但是,它的实际值可能取决于您正在使用的编译器。这是编译器警告背后的非可移植性的可能性。

好的,那么这会让我们得到什么?好吧,如果您准备对此方法将产生的假设可移植性问题负责,您可以告诉编译器您很高兴它使用标准规则进行转换。您可以使用强制转换

来完成此操作
hashval = ((unsigned char) *s) + 31 * hashval;

这种方法会抑制警告,并确保你的算术都是无符号的,这就是你想要的这种函数。但是,您需要注意其他系统上的相同代码可能会给出不同的哈希结果。

另一种方法是使用ANSI C标准指定可以有效地将指针强制转换为类型unsigned char *以访问指向的数据的基础字节结构这一事实。 (我现在还没有我的标准副本,或者我会给你一个参考。)这将允许你推广这种方法来生成一个函数,它给你一个任何数据值的哈希值类型。 (但是,为此,您必须考虑如何知道传入的数据的大小。)这可能类似于:

unsigned hash(void *s, size_t n) {
  unsigned char *t = (unsigned char *) s;

  while (n--)
    hashval = (*(t++) + 31 * hashval) % HASHSIZE;

  return hashval;
}

我希望这会让你对正在发生的事情有所了解。

答案 1 :(得分:2)

在功能签名中将s更改为unsigned char *,或在您使用时简单投射(即(unsigned char *)s)。

答案 2 :(得分:1)

我认为您可以安全地将您的char转换为unsigned :( unsigned char)* s