1

Mapping of string to int is working fine.

std::map<std::string, int> // working

But I want to map C-style string to int

For example:

char A[10] = "apple";
map<char*,int> mapp;
mapp[A] = 10;

But when I try to access the value mapped to "apple" I am getting a garbage value instead of 10. Why it doesn't behave the same as std::string?

Mephy
  • 2,978
  • 3
  • 25
  • 31
cryptomanic
  • 5,986
  • 3
  • 18
  • 30
  • 4
    That's because you don't compare strings in `operator[]`, but addresses of string constants. `std::string` is different thing. That's why in `C` you use `strcmp` function to compare strings, `operator[]` of `std::map` uses simple `==` to compare. – xinaiz Jul 18 '16 at 22:24
  • @Black Moses thanks I got the point. It means I will have to usr std::string in such scenerio's. – cryptomanic Jul 18 '16 at 22:28
  • 1
    No, it doesn't. It means that you have to provide a comparison function. – Marshall Clow Jul 18 '16 at 22:39
  • Agree with Marshall Clow , but `string` is all done for you and thus much easier. @BlackMoses Also agree with Mephy. Write that up, tack on a bit on how to use a comparator and you have yourself an upvotable answer. – user4581301 Jul 18 '16 at 22:45

2 Answers2

5
map<char*,int> mapp;

They key type here is not "c string". At least not, if we define c string to be "an array of characters, with null terminator". The key type, which is char*, is a pointer to a character object. The distinction is important. You aren't storing strings in the map. You are storing pointers, and the strings live elsewhere.

Unless you use a custom comparison function object, std::map uses operator<(const key_type&,key_type&) by default. Two pointers are equal if, and only if they point to the same object.

Here is an example of three objects:

char A[] = "apple";
char B[] = "apple";
const char (&C)[6] = "apple"

First two are arrays, the third is an lvalue reference that is bound to a string literal object that is also an array. Being separate objects, their address is of course also different. So, if you were to write:

mapp[A] = 10;
std::cout << mapp[B];
std::cout << mapp[C];

The output would be 0 for each, because you hadn't initialized mapp[B] nor mapp[C], so they will be value initialized by operator[]. The key values are different, even though each array contains the same characters.

Solution: Don't use operator< to compare pointers to c strings. Use std::strcmp instead. With std::map, this means using a custom comparison object. However, you aren't done with caveats yet. You must still make sure that the strings must stay in memory as long as they are pointed to by the keys in the map. For example, this would be a mistake:

char A[] = "apple";
mapp[A] = 10;
return mapp; // oops, we returned mapp outside of the scope
             // but it contains a pointer to the string that
             // is no longer valid outside of this scope

Solution: Take care of scope, or just use std::string.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • A good answer, except `mapp[A] = 10; std::cout << mapp[B]; std::cout << mapp[C];` does not output "garbage", it outputs `00` because `std::map::operator[]` inserts `value_type(key, T())` if `key` does not yet exist. – bcrist Jul 19 '16 at 01:35
0

It can be done but you need a smarter version of string:

struct CString {
    CString(const char *str) {
        strcpy(string, str);
    }
    CString(const CString &copy); // Copy constructor will be needed.
    char string[50]; // Or char * if you want to go that way, but you will need
                     // to be careful about memory so you can already see hardships ahead.
    bool operator<(const CString &rhs) {
        return strcmp(string, rhs.string) < 0;
    }
}

map<CString,int> mapp;
mapp["someString"] = 5;

But as you can likely see, this is a huge hassle. There are probably some things that i have missed or overlooked as well.

You could also use a comparison function:

struct cmpStr{
    bool operator()(const char *a, const char *b) const {
        return strcmp(a, b) < 0;
    }
};

map<char *,int> mapp;
char A[5] = "A";
mapp[A] = 5;

But there is a lot of external memory management, what happens if As memory goes but the map remains, UB. This is still a nightmare.

Just use a std::string.

Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175