我正在使用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
签名,我会得到负哈希值。我想知道如何解决这个问题。
任何帮助?
答案 0 :(得分:4)
您的编译器正在接受并警告您,您隐式更改了对s
指向的区域中存储的字节的解释。函数原型指定s
为指向char
的指针,默认情况下,您的设置char
似乎已签名。但是,要使算术运算正确,您只需要使用无符号值。所以问题是:编译器应该通过实际具有负值的s
指向的值做什么?
让我们快速转移,以确保我们了解我们可能正在考虑的价值观。 signed char
的可能值为CHAR_MIN
到CHAR_MAX
。 (这些值可以在limits.h
中找到。)unsigned char
的可能值为0
到UCHAR_MAX
。所以问题就变成了这样:我们如何在CHAR_MIN
到CHAR_MAX
范围内表示从0
到UCHAR_MAX
的可能值范围?
一个简单的方法就是让编译器为您执行此转换:它只是使用环绕算法来确保该值在限制范围内:它会自动添加UCHAR_MAX + 1
足够的时间来获取一个值在0
到UCHAR_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