std::string::c_str() overwrittes the previous one

2019-09-20 17:36发布

问题:

I could not understand that how can be the pointers same when the text size is equal. It seems like firstStringObj::c_str() overwrittes the previous one's pointer.

#include <iostream>
#include <string>
#include <string>
#include <stdio.h>

std::string getConstCharAndModifyItWithANewString( const char* constchar )
{
    std::string stringAtStack( constchar );
    stringAtStack += "::isModified";
    return stringAtStack;
}

int main()
{
    const char* firstConstCharPointer = getConstCharAndModifyItWithANewString("Hi!").c_str();
    std::string firstStringObj = "Hi+";

    printf(" firstConstCharPointer(%s)(%p)\nfirstStringObj(%s)(%p)\n\n", firstConstCharPointer,firstConstCharPointer, firstStringObj.c_str(),     firstStringObj.c_str()  );
}

OUTPUT: firstConstCharPointer(Hi+)(0x4593eb8) firstStringObj(Hi+)(0x4593eb8)

回答1:

Your function returns a temporary std::string object. After the firstConstCharPointer variable is assigned and the expression is complete, that temporary object gets destroyed, freeing its allocated memory block and leaving the variable pointing at freed memory. This is known as a dangling pointer.

firstStringObj then allocates a new memory block, which happens to be reusing the same memory block that the temp std::string had previously allocated and freed. So the dangling pointer happens to now be pointing at valid memory again. That is why your printf() statement is able to display the same memory address and content for both strings.

But this is undefined behavior. The memory block that gets allocated each time is completely up to the string's Allocator to decide. The second std::string could just as easily have allocated a completely different memory block, and then the code would be more likely to crash, or at least print garbage, when it tries to dereference the dangling pointer that is still pointing at invalid memory.

In order for your code to work, you need to change firstConstCharPointer to a std::string object so the temp std::string gets copied properly, eg:

#include <iostream>
#include <string>
#include <cstdio>

std::string getConstCharAndModifyItWithANewString( const char* constchar )
{
    std::string stringAtStack( constchar );
    stringAtStack += "::isModified";
    return stringAtStack;
}

int main()
{
    const std::string firstConstStringObj = getConstCharAndModifyItWithANewString("Hi!");
    std::string secondStringObj = "Hi!";

    std::printf(" firstConstStringObj(%s)(%p)\nsecondStringObj(%s)(%p)\n\n", firstConstStringObj.c_str(), firstConstStringObj.c_str(), secondStringObj.c_str(), secondStringObj.c_str());
}


回答2:

The pointers are the same on your platform because firstConstCharPointer is a dangling pointer, it points to deallocated memory.

That's because the std::string returned by getConstCharAndModifyItWithANewString is destroyed after assignment expression const char* firstConstCharPointer = ...;.

So when you create a new std::string object, the compile choose to use the same memory location as the previous std::string object, and so the pointers are the same.

On my platform for example the pointers are the same, and they're not in Ideone.



回答3:

You have classic undefined behaviour. printf tries to derefenrece firstConstCharPointer because of %s. firstConstCharPointer points to data that has been destroyed because the std::string associated with this pointer's lifetime stops after the assignment:

const char* firstConstCharPointer = getConstCharAndModifyItWithANewString("Hi!").c_str();
// temporary std::string returned from getConstCharAndModifyItWithANewString destroyed, pointer becomes dangling.


回答4:

As stated in documentation:

The pointer obtained from c_str() may be invalidated by:

  • Passing a non-const reference to the string to any standard library function, or
  • Calling non-const member functions on the string, excluding operator[], at(), front(), back(), begin(), rbegin(), end() and rend().

So you are using invalid pointer, as destructor is a non-const member function and is not listed above.