Search code examples
c++stringmemory-managementheap-memorystack-memory

Is string.c_str() deallocation necessary?


My code converts C++ strings to C strings somewhat often, and I am wondering if the original string is allocated on the stack. Will the C string be allocated on the stack as well? For instance:

string s = "Hello, World!";
char* s2 = s.c_str();

Will s2 be allocated on the stack, or in the heap? In other words, will I need to delete s2?

Conversely, if I have this code:

string s = new string("Hello, mr. heap...");
char* s2 = s.c_str();

Will s2 now be on the heap, as its origin was on the heap?

To clarify, when I ask if s2 is on the heap, I know that the pointer is on the stack. I'm asking if what it points to will be on the heap or the stack.


Solution

  • string s = "Hello world";
    char* s2 = s.c_str();
    

    Will s2 be allocated on the stack, or in the heap? In other words... Will I need to delete s2?

    No, don't delete s2!

    s2 is on the stack if the above code is inside a function; if the code's at global or namespace scope then s2 will be in some statically-allocated dynamically-initialised data segment. Either way, it is a pointer to a character (which in this case happens to be the first 'H' character in the null-terminated string_ representation of the text content of s). That text itself is wherever the s object felt like constructing that representation. Implementations are allowed to do that however they like, but the crucial implementation choice for std::string is whether it provides a "short-string optimisation" that allows very short strings to be embedded directly in the s object and whether "Hello world" is short enough to benefit from that optimisation:

    • if so, then s2 would point to memory inside s, which will be stack- or statically-allocated as explained for s2 above
    • otherwise, inside s there would be a pointer to dynamically allocated (free-store / heap) memory wherein the "Hello world\0" content whose address is returned by .c_str() would appear, and s2 would be a copy of that pointer value.

    Note that c_str() is const, so for your code to compile you need to change to const char* s2 = ....

    You must notdelete s2. The data to which s2 points is still owned and managed by the s object, will be invalidated by any call to non-const methods of s or by s going out of scope.

    string s = new string("Hello, mr. heap...");
    char* s2 = s.c_str();
    

    Will s2 now be on the heap, as its origin was on the heap?

    This code doesn't compile, as s is not a pointer and a string doesn't have a constructor like string(std::string*). You could change it to either:

    string* s = new string("Hello, mr. heap...");
    

    ...or...

    string s = *new string("Hello, mr. heap...");
    

    The latter creates a memory leak and serves no useful purpose, so let's assume the former. Then:

    char* s2 = s.c_str();
    

    ...needs to become...

    const char* s2 = s->c_str();
    

    Will s2 now be on the heap, as its origin was on the heap?

    Yes. In all the scenarios, specifically if s itself is on the heap, then:

    • even if there's a short string optimisation buffer inside s to which c_str() yields a pointer, it must be on the heap, otherwise
    • if s uses a pointer to further memory to store the text, that memory will also be allocated from the heap.

    But again, even knowing for sure that s2 points to heap-allocated memory, your code does not need to deallocate that memory - it will be done automatically when s is deleted:

    string* s = new string("Hello, mr. heap...");
    const char* s2 = s->c_str();
    // <...use s2 for something...>
    delete s;   // "destruct" s and deallocate the heap used for it...
    

    Of course, it's usually better just to use string s("xyz"); unless you need a lifetime beyond the local scope, and a std::unique_ptr<std::string> or std::shared_ptr<std::string> otherwise.