7

For Code:

stringstream ss("012345678901234567890123456789012345678901234567890123456789");  

some articles said it is wrong for followed usage due to ss.str return temp object and will destructered before call .c_str();

 const char* cstr2 = ss.str().c_str();  

but I run the example, there is no problem? how to understand?

jiafu
  • 6,338
  • 12
  • 49
  • 73

3 Answers3

13

But I run the example, there is no problem?

Technically there's nothing immediately wrong in the expression:

const char* cstr2 = ss.str().c_str();

The temporary (copy) object returned by ss.str() will live long enough to let you get the underlying c-string with c_str().

However, the result is likely a dangling pointer: by the end of the expression, the string object will probably be destroyed already, so the underlying memory may be deallocated (this depends heavily on the std::basic_string implementation).

Therefore, this approach should be avoided since it's impossible to safely use the resulting pointer. What you should do instead is:

auto x = ss.str();
const char* cstr2 = x.c_str(); 

The above code won't get you any trouble, since the returned value of str() is now being copied/is not a temporary anymore, and the access to x.c_str() will give you a valid pointer.

Coder-256
  • 5,212
  • 2
  • 23
  • 51
Shoe
  • 74,840
  • 36
  • 166
  • 272
  • 17
    Your answer: "There's nothing wrong. There's probably something wrong. Here's how to make your code not wrong any more." – Lightness Races in Orbit Jan 10 '14 at 03:18
  • 1
    @LightnessRacesinOrbit, my answer "There's nothing wrong in this expression. But the result of the expression is a problem: here's how to fix it". – Shoe Jan 10 '14 at 03:19
  • 4
    I suppose if you'd only quoted the expression I'd agree. But you quoted the whole _statement_, and the _statement_ is not okay because it's (a) useless and (b) dangerous :) – Lightness Races in Orbit Jan 10 '14 at 03:20
  • @LightnessRacesinOrbit That's why I used the word "*expression*" in "there's nothing wrong in the expression". – Shoe Jan 10 '14 at 03:22
6

Yet another wonderful C++ landmine.

Basically, your pointer references a block of memory (C string) that is referencing a temporary copy (string) of whatever was in the stream at the time you did the double affectation.

str() returns a temporary object.

Life expectancy of temporary objects is rather short. Either someone takes a reference to them immediately (e.g. string& s = ss.str()) or they die at the end of the statement they were born in.

However, the compiler allows to get a reference to the same block of memory through c_str(), but indirectly, so it flies under the compiler's radar. What a pity.

So your pointer will indeed be valid at the time c_str gets it, but just for about as long as if you did something like this in plain old C:

const char * cstr2;
{
    char ss_str[100];            // str() return value is dynamically created
    char * c_str = &ss_str_[10]; // c_str() takes a reference to some part of it
    cstr2 = c_str;               // cstr2 takes an indirect reference to str() ret. val.
}                                // compiler pulls the plug on str() ret. val.

So, right after the end of this very statement, c_str is already referencing the cadaver of whatever string str() returned.

Now since temporary objects are allocated on the stack, you may well never notice the problem. Object deallocation in itself will likely not modify the defunct string value, but as soon as the compiler will reuse this bit of stack, the proverbial guru will have something to meditate over.

kuroi neko
  • 8,479
  • 1
  • 19
  • 43
  • It looks like the "this bit of IBM lore" link is useless now. It gets automatically redirected to the IBM knowledge center without any reference to the original page/article. – Dennis van der Schagt Dec 16 '17 at 13:36
  • 1
    ah well, that's web reliability for you... :) – kuroi neko Dec 16 '17 at 23:05
  • You can not take a reference of the string, you actually have to copy the returned object! The reference will become invalid at the end of the function call due to the exact same reasons as why you can't use the returned value of `c_str()`. – Ruud Althuizen Jan 07 '19 at 10:23
4

First of all, it's important to understand that attempting to make use of a dangling pointer is not guaranteed to fail in any obvious way. It can appear to "work" just as often as it can crash spectacularly.

Secondly, yes, that code is invalid and you shouldn't do it. The stringstream's lifetime is not important because std::stringstream::str() returns by value (i.e. a copy of the internal buffer), but then you're still subject to that string going out of scope before you can make use of its C-string pointer.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I think it worthwhile to point out that in a slightly modified context the code is legit MessageBox(..., const char*); MessageBox(..., stream.str().c_str()); – Werner Erasmus Apr 09 '21 at 14:31