Search code examples
cequalitycstringstrdup

c++ char* converted from a string using strdup doesn't equal original raw string


What I'm wondering is why converting a string to a char* seems to make the new char* not equal to the literal string it came from.

If I have:

//raw versions of the string:
string s = "fun";
char* c = "fun";

char* s_convert = strdup(s.c_str()); //converting the string to char*

printf("(string) == 'fun' -> %d\n", (s == "fun"));
printf("(char*) == 'fun' -> %d\n", (c == "fun"));
printf("(char* convert) == 'fun' -> %d\n", (s_convert == "fun"));

printf("(string) == (char*) -> %d\n", (s == c)); //does new char* equal original string

produces:

(string) == 'fun' -> 1 //true
(char*) == 'fun' -> 1  //true
(char* convert) == 'fun' -> 0 //false
(string) == (char* convert) -> 1 //true

So the converted char* still equals the original string it came from. But for some reason char* s_convert doesn't equal the literal string that it came from, although the original string s does.

Why does this happen? And is there a better way I can convert a string to char* that won't cause this?


Solution

  • Let's go through your comparisons one by one:

    printf("(string) == 'fun' -> %d\n", (s == "fun"));
    

    Compares a std::string variable with a string literal. Works as expected (i.e. really checks whether s contains the string "fun"), because std::string has an overloaded operator== for that purpose.

    printf("(char*) == 'fun' -> %d\n", (c == "fun"));
    

    Compares the address stored in c with the address of the string literal "fun". The result can be true or false. In your case, it is true, because the compiler optimized your code: It saw you use "fun" multiple times, and only stores the string literal once in memory, so the address will always be the same. If, e.g., c had been assigned the literal "fun" in a different translation unit, the result would be false.

    printf("(char* convert) == 'fun' -> %d\n", (s_convert == "fun"));
    

    Compares the address stored in s_convert to the address of the string literal "fun". As mentioned above, the string literal "fun" has a particular address chosen by your compiler. It will not compare equal to an explicitly copied string (it's irrelevant that it comes from the std::string variable). The strdup creates newly allocated memory, so the address will not compare equal to anything that existed in memory before.

    printf("(string) == (char*) -> %d\n", (s == c)); //does new char* equal original string
    

    Again, compares an std::string to a C-style string. This really compares the strings and checks whether s contains the null-terminated string stored at the address c thanks to the overloaded operator== of std::string, so the result is true.

    To summarize: The effects that you observe have nothing or little to do with the conversions between std::string and null-terminated C-style strings. You just don't compare what you think you compare. Use strcmp if you want to compare null-terminated "raw" strings, or use == when you compare std::string to something else, and you will bet the results that you expect.