cryptomanic
cryptomanic

Reputation: 6306

Map C-style string to int using C++ STL?

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?

Upvotes: 1

Views: 1794

Answers (2)

eerorika
eerorika

Reputation: 238351

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.

Upvotes: 5

Fantastic Mr Fox
Fantastic Mr Fox

Reputation: 33864

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.

Upvotes: 0

Related Questions