1

The following code with Visual Studio 2013 produces unexpected results when getting a const char * directly from a std::ostringstream but correct results when using a intermediate std::string. Why is that?

#include <string>
#include <sstream>
#include <string>
#include <iostream>

int main()
{
    std::ostringstream oss;
    oss << "Hello work" << std::endl;

    const char *charPtr = oss.str().c_str(); // Unexpected results

    std::string str = oss.str();
    const char *charPtr2 = str.c_str(); // OK

    std::cout << "charPtr is: " << charPtr << std::endl;
    std::cout << "charPtr2 is: " << charPtr2 << std::endl;

    char c;
    std::cin >> c;

    return 0;
}

Produces

charPtr is:
charPtr2 is: Hello work
Korchkidu
  • 4,908
  • 8
  • 49
  • 69

4 Answers4

8

The pointer returned by c_str() is managed by the std::string instance over which c_str() is called.

Now, in that line, you are calling c_str() over the return value of str(), which is a temporary value that is destroyed at the end of the statement.

So, your resulting const char * variable ends up containing a pointer to already freed memory, which gives all sort of problems; technically, it's undefined behavior, i.e. anything can happen, including "seemingly work" if that memory hasn't been already reassigned/overwritten.

If instead you save the result of str() you don't have problems, since, as long as the std::string variable is alive, it will keep alive its buffers as well (but remember that any modification of an std::string invalidates any previous pointer returned by c_str()).

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
2
const char *charPtr = oss.str().c_str(); // Unexpected results

oss.str() returns a temporary string. Temporaries are destroyed at the end of the full-expression that creates them - in this case, at the end of the statememt. This leaves charPtr dangling, pointing to memory that was deallocated when the temporary string was destroyed. Using that pointer gives undefined behaviour.

std::string str = oss.str();
const char *charPtr2 = str.c_str(); // OK

Now you're assigning the string to a variable, with automatic storage duration. This won't be destroyed until it goes out of scope, so the pointer is safe to use.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Ok, so this is Undefined Behaviour. That's why it works with GCC and not with Visual Studio 2013 (bugs excluded)? – Korchkidu Nov 20 '13 at 13:16
  • 1
    @Korchkidu: You'll probably get different outcomes on different compilers, operating systems, optimisation settings, or phases of the moon, and some of these might match what you'd expect if the code "worked". It will depend on what happened to the deallocated memory before you access it - maybe it still contains the old string, maybe it's been reused and overwritten, or maybe it's been unmapped and will cause a crash. – Mike Seymour Nov 20 '13 at 13:24
1

Don't forget that some methods can return temporary entity which lifetime is limited by nearest ";" delimiter (end of statement).

So once you remember charPtr, it still alive but becomes invalid after the end of statement. charPtr2 is more lucky because you create a copy of the string first, then capture the pointer. But anyway, take a look at this topic for detailed explanation, why you're not playing safe: What is std::string::c_str() lifetime?

Community
  • 1
  • 1
Yury Schkatula
  • 5,291
  • 2
  • 18
  • 42
1

To further corroborate the other answers, take a look at this link.

Return value: For (1), a string object with a copy of the current contents in the stream buffer.