52

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Georges Oates Larsen
  • 6,812
  • 12
  • 51
  • 67
  • Does this answer your question? [Does a pointer returned by std::string.c\_str() or std::string.data() have to be freed?](https://stackoverflow.com/questions/7460753/does-a-pointer-returned-by-stdstring-c-str-or-stdstring-data-have-to-be) – imz -- Ivan Zakharyaschev Feb 19 '20 at 18:21

7 Answers7

48
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.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
18

c_str() returns a pointer to an internal buffer in the string object. You don't ever free()/delete it.

It is only valid as long as the string it points into is in scope. In addition, if you call a non-const method of the string object, it is no longer guaranteed to be valid.

See std::string::c_str

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Brian Roach
  • 76,169
  • 12
  • 136
  • 161
  • 1
    Interesting, so, you would never free the char* because doing that would free internal data in the string? c_str() literally holds the same address as the actual data the string is using? Multiple calls to .c_str() would return the same address in that way? (Just clarifying so I know that I understand) – Georges Oates Larsen Jan 12 '12 at 23:03
  • @Georges the answer to some of those questions depends on whether you're using the latest standard or the old standard. You may want to clarify which one are you interested in. – R. Martinho Fernandes Jan 12 '12 at 23:05
  • @R.MartinhoFernandes Better go with the latest standard. Would be interesting though to know just how recent this standard is – Georges Oates Larsen Jan 12 '12 at 23:06
  • 2
    "It is only valid as long as the string it points into is." And so long as you do not mutate the string that owns the buffer. – James McNellis Jan 12 '12 at 23:06
  • 1
    Correct - Bad Things would happen if you freed it. As for multiple calls I believe it would be the same address until a non-const method were called on the string object, then it's no longer guaranteed. – Brian Roach Jan 12 '12 at 23:06
  • @JamesMcNellis - also true, I should note that – Brian Roach Jan 12 '12 at 23:07
4

Firstly, even your original string is not allocated on the stack, as you seem to believe. At least not entirely. If your string s is declared as a local variable, only the string object itself is "allocated on the stack". The controlled sequence of that string object is allocated somewhere else. You are not supposed to know where it is allocated, but in most cases it is allocated on the heap. I.e. the actual string "Hello world" stored by s in your first example is generally allocated on the heap, regardless of where you declare your s.

Secondly, about c_str().

In the original specification of C++ (C++98) c_str generally returned a pointer to an independent buffer allocated somewhere. Again, you are not supposed to know where it is allocated, but in general case it was supposed to be allocated on the heap. Most implementations of std::string made sure that their controlled sequence was always zero-terminated, so their c_str returned a direct pointer to the controlled sequence.

In the new specification of C++ (C++11) it is now required that c_str returns a direct pointer to the controlled sequence.

In other words, in general case the result of c_str will point to a heap-allocated memory even for local std::string objects. Your first example is not duifferent from your second example in that regard. However, in any case the memory pointed by c_str() is not owned by you. You are not supposed to deallocate it. You are not supposed to even know where it is allocated.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Re *"The controlled sequence of that string object is allocated somewhere else."*: Doesn't that entirely depend on the implementation (e.g., [SSO](https://stackoverflow.com/questions/10315041/meaning-of-acronym-sso-in-the-context-of-stdstring/10319672#10319672))? As in [R. Martinho Fernandes's answer](https://stackoverflow.com/questions/8843604/is-string-c-str-deallocation-necessary/8843723#8843723)? – Peter Mortensen Feb 05 '23 at 22:49
3

std::string::c_str() returns a const char*, not a char *. That's a pretty good indication that you don't need to free it. Memory is managed by the instance (see some details in this link, for example), so it's only valid while the string instance is valid.

vanza
  • 9,715
  • 2
  • 31
  • 34
  • 7
    The const-qualification of the pointed-to object is not at all an indication that it doesn't need to be freed by the caller. – James McNellis Jan 12 '12 at 23:08
  • @JamesMcNellis: true for objects. But a `char *` is not exactly an object, just a plain C type. And in plain C constness is generally used to indicate that "you do not own this memory" (since `free(3)` takes a non-const pointer). – vanza Jan 12 '12 at 23:37
  • @JamesMcNellis: what examples of such functions are there? I'd consider it very confusing and would comment very clearly when the pointed-to object of a returned `const` pointer needed to be freed by the caller. – leftaroundabout Jan 12 '12 at 23:45
2

s2 will be valid as long as s remains in scope. It's a pointer to memory that s owns. See e.g. this MSDN documentation: "the string has a limited lifetime and is owned by the class string."

If you want to use std::string inside a function as a factory for string manipulation, and then return C-style strings, you must allocate heap storage for the return value. Get space using malloc or new, and then copy the contents of s.c_str().

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
japreiss
  • 11,111
  • 2
  • 40
  • 77
1

Will s2 be allocated on the stack, or in the heap?

Could be in either. For example, if the std::string class does small string optimization, the data will reside on the stack if its size is below the SSO threshold, and on the heap otherwise. (And this is all assuming the std::string object itself is on the stack.)

Will I need to delete s2?

No, the character array object returned by c_str is owned by the string object.

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

In this case the data will likely reside in the heap anyway, even when doing SSO. But there's rarely a reason to dynamically allocate a std::string object.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
0

That depends. If I remember correctly, CString makes a copy of the input string, so no, you wouldn't need to have any special heap allocation routines.

Chris K
  • 11,996
  • 7
  • 37
  • 65
  • I think he meant [C string](https://en.wikipedia.org/wiki/C_string), not *CString* (name of a C++ class on Windows? [MFC](https://en.wikipedia.org/wiki/Microsoft_Foundation_Class_Library)?). The type of the result in the example is pointer to *char*. – Peter Mortensen Feb 05 '23 at 22:39