C++ unordered_map with char* key produces unexpected behavior

試著忘記壹切 提交于 2019-12-13 06:12:54

问题


I attempted to use an unordered_map to hash a char* key to an integer value. After writing custom functors to hash and compare char*, the unordered map appeared to work. However, I eventually noticed that the hash would occasionally return incorrect results. I created a test project to reproduce the error. The code below creates an unordered_map with a char* key and custom functors. It then runs 1000x cycles and records any hash errors that occurred. I am wondering if there is something wrong with my functors, or if the problem lies within unordered_map. Any help would be appreciated. Thanks!

#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <tr1/unordered_map>

using namespace std;

//These varaibles are just used for printing the status.
static const char* c1;
static const char* c2;
static int cmpRet;
static int cmpVal;
static const char* hashChar;
static size_t hashVal;

// Character compare functor.
struct CmpChar {

  bool operator()(const char* s1, const char* s2) const {
    c1 = s1;
    c2 = s2;
    cmpVal = strcmp(s1, s2);
    cmpRet = (cmpVal == 0);
    return cmpRet;
  }
};

// Hash functor.
struct HashChar {

  size_t operator()(const char* str) const {
    hashChar = str;
    size_t hash = 0;
    int c;

    while (c = *str++)
      hash = c + (hash << 6) + (hash << 16) - hash;

    hashVal = hash;
    return hash;
  }
};

void printStatus() {
  printf("'%s' was hashed to: '%lu'\n", hashChar, hashVal);
  printf("strcmp('%s','%s')='%d' and KeyEqual='%d'\n", c1, c2, cmpVal, cmpRet);
}

int main(int argc, char** argv) {

  // Create the unordered map.
  tr1::unordered_map<const char*, int, HashChar, CmpChar > hash_map;
  hash_map["apple"] = 1;
  hash_map["banana"] = 2;
  hash_map["orange"] = 3;

  // Grab the inital hash value of 'apple' to see what it hashes to.
  char buffer[256];
  bzero(buffer, sizeof (buffer));
  strcpy(buffer, "apple");
  if (hash_map[buffer] == 1) {
    printf("First hash: '%s'=1\n", buffer);
  }
  printStatus();

  // Create a random character
  srand((unsigned int) time(NULL));
  char randomChar = (rand() % 26 + 'a');

  // Use the hash 1000x times to see if it works properly.
  for (int i = 0; i < 1000; i++) {

    // Fill the buffer with 'apple'
    bzero(buffer, sizeof (buffer));
    strcpy(buffer, "apple");

    // Try to get the value for 'apple' and report an error if it equals zero.
    if (hash_map[buffer] == 0) {
      printf("\n****Error: '%s'=0 ****\n", buffer);
      printStatus();
    }

    // Fill the buffer with a random string.
    bzero(buffer, sizeof (buffer));
    buffer[0] = randomChar;
    buffer[1] = '\0';

    // Hash the random string.
    // ** Taking this line out removes the error. However, based on the functors
    // it should be acceptable to reuse a buffer with different content.
    hash_map[buffer];

    // Update the random character.
    randomChar = (rand() % 26 + 'a');
  }

  printf("done!\n");

  return EXIT_SUCCESS;
}

回答1:


You must be really careful when using char* in containers, as the char* won't be copied as you may hope.

By using the operator[] of unordered_map what is used as the key in the map is not the string you want.

operator[] is supposed to insert the key into the map, copying it invoking the default constructor (see the reference), in this case, it will simply copy buffer[0].

So afterwards, your method CmpChar will have a strange behaviour, as the next bytes it'll read in the keys can be anything.

You would not have such problems if using string objetcs.



来源:https://stackoverflow.com/questions/17506759/c-unordered-map-with-char-key-produces-unexpected-behavior

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!