哈希溢出

时间:2012-12-30 12:27:23

标签: c++ hash integer-overflow

int hash (const string &key, int tableSize) {
   int hashVal = 0; 

   for (int i = 0; i < key.length(); i++)
        hashVal = 37*hashVal + key[i]; 
   hashVal %= tableSize; 
   if (hashVal < 0)   /* in case overflows occurs */
        hashVal += tableSize; 

   return hashVal;      
};

为什么我们控制hashVal是否小于零?这怎么可能?

5 个答案:

答案 0 :(得分:2)

您可以在变量hashVal中获得溢出。这(有时)导致负值。例如,尝试在C ++程序中打印3 * 1000 * 1000 * 1000的值:

std::cout << 3 * 1000 * 1000 * 1000;

在我的计算机上,使用我的编译器,打印-1294967296。

结果3000000000是二进制的10110010110100000101111000000000,但由于此特定平台上的整数为32位,我们使用二进制补码方法表示负数,此位模式表示负数。 / p>

标准将整数溢出定义为未定义的行为,因此实际上可能发生任何事情,但这是典型的效果。

答案 1 :(得分:2)

如果字符串足够长,则代码为:

for (int i = 0; i < key.length(); i++)
    hashVal = 37*hashVal + key[i]; 

可能会导致hashVal的值超过int的最大值(通常类似于2 31 - 1)并变为负值。这称为integer overflow

C ++标准does not specify负操作数的%运算符的值是正数还是负数;因此,根据您的编译器和CPU架构(以及可能的编译时开关),-47 % 37之类的表达式可以评估为-1027。因此,你引用的代码通过在结果中添加模数来防止前者的可能性。

顺便说一句,避免此问题的一种更简单的方法是将hashVal定义为无符号。

答案 2 :(得分:0)

如果密钥足够长,hashVal值可能会变为负数。您可以尝试使用不同长度的字符串(例如“1”,“11”,“111”,“1111”等)来查看hashVal将变为负数的位置(大约5-7个字符就足够了)。

然后你尝试得到负数的模数,这也是负数。但你不能指向负数组索引(看起来,这个函数计算要存储的字符串的位置),所以你把它作为数组索引是正面的和适当的。

答案 3 :(得分:0)

hashValfor循环中变得越来越大,它很容易变得比最大的signed int值大,后者取决于平台。 如果hashVal循环后for为负数,则%=运算符后仍可能为负数,这也是平台相关的(在某些情况下,它总是返回非负值,而它可能会返回否则,你需要检查hashVal之后是否为否定。

答案 4 :(得分:0)

尝试按以下方式调用哈希函数

hash("HelloHello",100);

然后逐步执行程序或在哈希函数中打印一条消息,看看哈希值是否低于0。

例如,在for循环中你可以放

if(hashVal < 0)
{
    cout<<"OVERFLOW HAS HAPPENED\n";
    break;
}

你会看到hashVal低于0。