C++ C++;字符串哈希是对字符串还是内存地址进行哈希?
我以前从未研究过散列算法,当使用std::unordered_map时,我惊讶地发现散列函数(我想)实际上是散列内存地址,而不是字符串。如果我错了,请纠正我,但我只是通过更改一个原始字符串并将其添加到无序的_映射中发现了这一点,当内存地址(指针)相同时,它从未添加任何内容 在以下情况下,是否添加新键取决于std::string是否重新分配到内存的另一个区域:C++ C++;字符串哈希是对字符串还是内存地址进行哈希?,c++,string,dictionary,hash,std,C++,String,Dictionary,Hash,Std,我以前从未研究过散列算法,当使用std::unordered_map时,我惊讶地发现散列函数(我想)实际上是散列内存地址,而不是字符串。如果我错了,请纠正我,但我只是通过更改一个原始字符串并将其添加到无序的_映射中发现了这一点,当内存地址(指针)相同时,它从未添加任何内容 在以下情况下,是否添加新键取决于std::string是否重新分配到内存的另一个区域: std::unordered_map<const char*, char*> myMap; std::string mySt
std::unordered_map<const char*, char*> myMap;
std::string myString = "Key1";
myMap[myString.c_str()] = "someVal"; // <--- Adds a new key, size is now 1
myString = "Key2";
myMap[myString.c_str()] = "someVal"; // <--- Doesn't add a new key "Key2" didn't need to be reallocated
std::无序映射myMap;
std::string myString=“Key1”;
myMap[myString.c_str()]=“someVal”// 您错误地认为constchar*
是一个字符串。它实际上是一个指针。因此,std::unordered_map
使用指针(类型为const char*
)作为键,std::hash
对指针(对地址进行散列)的专门化作为散列键
如果要使用字符串作为键,应使用std::string
,例如std::unordered_map
编辑我还应该说,使用指针而不是字符串至少是危险的,但通常是不可能的。它不会像你想的那样。问题在于,字符串(字符序列)及其地址(指针)在程序的生命周期内不一定是成对的(尽管对于某些const char*
对象可能是这样)。想想下面这些
std::unordered_map<const char*,int> map;
char str[11] = "bad";
map[str] = 2; // hashes str = char*
auto x = map["bad"]; // hashes address of "bad"; x!=2
std::无序地图;
char str[11]=“坏”;
map[str]=2;//hashes str=char*
自动x=映射[“坏”];//散列“bad”的地址;x=2.
这说明了使用地址作为键并没有达到预期效果:如果查看的是标准的基本专门化,则无法从字符序列(“bad”
)中获取元素。constchar*
没有专门化,因为它只是指向字符数组的指针。但是,任何指针类型都有专门化:
template< class T > struct hash<T*>;
由于键是const char*
,因此代码运行正常。
尝试使用std::string
作为键来获取所需的行为
所以:std::无序映射myMap代码>使用指针作为键可能是一种解决方案,但仅适用于常量字符串-指针是最简单和最快的哈希。您可以使用不同的常量变量初始化无序映射,确保它们的生存期是适当的
C++字符串哈希是散列字符串还是内存地址?< /p>
这个问题实际上是关于平等和身份的,取决于你说“字符串”的意思
- 相等。如果您指的是
std::string
类,则哈希与内存地址无关。字符串的实际内容是散列的。两个std::string
实例相等,如果内容彼此相等,则生成相同的哈希
- 标识。如果您指的是指向内存中某些字符的指针,那么不管存储在内存中的数据是什么,内存地址都会被散列。两个“字符串”是相同的,如果它们指向相同的内存位置,则生成相同的哈希
处理字符串时,几乎总是希望进行相等的比较,并鼓励使用std::string
,因为代表相同数据的两个不同字符串实例应被视为相等,即使数据位于不同的内存地址,而std::string
总是为您提供这些语义,无论是通过哈希还是通过简单的比较,如myStr1==myStr2
[*]
哈希char const*
或char*
非常危险,因为您会遇到许多已实现的定义行为。字符串文字是这方面的主要示例。例如,考虑下面的程序:
#include <iostream>
int main()
{
char const *a = "foo";
char const *b = "foo";
std::cout << reinterpret_cast<void const*>(a) << "\n";
std::cout << reinterpret_cast<void const*>(b) << "\n";
}
您的代码还实现了定义的行为;不是因为文字,而是以不同的方式:
在以下情况下,是否添加新密钥取决于
std::string
是否重新分配到另一个内存区域:
std::unordered_map<const char*, char*> myMap;
std::string myString = "Key1";
myMap[myString.c_str()] = "someVal"; // <--- Adds a new key, size is now 1
myString = "Key2";
myMap[myString.c_str()] = "someVal"; // <--- Doesn't add a new key "Key2" didn't need to be reallocated
对。决不能从两个不同的std::string
实例中获取c_str()
指针,并假定这些指针完全相同,只是因为std::string
实例相等
如果必须对字符串本身进行散列,这种方法会更慢吗
不。我向您提出挑战,让您拿出一个实际的用例,您可以实际测量差异。只有这样,它才会“慢”一点。否则,这就是老生常谈的过早优化
但还有更多。从技术上讲,散列单个地址应该比使用整个字符串内容(或其中很大一部分)来计算散列值要快,因为涉及的数据更多。这很明显。但我不确定您是否看到执行“昂贵”计算的必要性。这里面没有魔法。如果程序逻辑关心字符串的内容,则必须考虑单个字符。即使在理论上,您应该如何散列未读取的数据
或者,更一般地说,如何散列你没有的东西
[P>[*]巧合的是,在java中,一个非常常见的错误是不考虑这种区别,即<代码> STR1= = STR2< /COD>具有不同的语义,而不是代码> STR1。(Str2)。代码> char */COD>不是字符串。是的,我知道,它是一个指向字符串的指针。我已经习惯将char*视为一个字符串,当您在需要字符串的地方传递它们时。那么std::string是唯一对实际字符串本身进行哈希处理的情况吗?我想在这种情况下,当在地图中查找时,哈希和查找都会变慢?想一想,如果是这样的话,难道不使用简单的poi吗
#include <iostream>
#include <unordered_map>
int main()
{
char const *a = "foo";
char const *b = "foo";
std::unordered_map<char const*, char*> myMap;
myMap[a] = "1";
myMap[b] = "2";
std::cout << myMap.size() << "\n"; // prints 1 or 2
}