1

So I have that simple code inside button click method:

std::stringstream ss;
unsigned counter = 0;
while(true)
{
    ss.clear();
    ss << DEFAULT_USER_CONFIG_NAME << " " << ++counter;
    const char* name = ss.str().c_str();
    MessageBox(name);

    /* ... while break condition */
}

The problem is that messagebox is empty. But it works correctly when I pass text directly:

MessageBox(ss.str().c_str()); // that shows text just fine

What I found with debugger is that local variable "name" is not created(at least it is not showing in debugger). Any clue why it is working when passed directly and failing otherwise? Also when i casted "name" to CString it returned true on IsEmpty() check.

Arrekin
  • 95
  • 5
  • 1
    All is according to the standard. The problem with the first version is that `ss.str()` is destructed before `MessageBox(name)` call. Do you have any question? – LogicStuff Sep 12 '16 at 14:04

1 Answers1

5

The expression ss.str() creates a temporary std::string object. Storing the result of c_str() thus points to memory of a temporary, which quickly turns into a dangling pointer. Once the full expression statement

const char* name = ss.str().c_str();
//                                 ^ this is where the temporary ss.str() gets destroyed.

is evaluated, the temporary gets destroyed.

You already know, how to solve this, by placing the expression creating the temporary inside the full expression, that consumes it. This extends the lifetime of the temporary until the end of the full expression:

MessageBox(ss.str().c_str());
//                          ^ this is where the temporary ss.str() gets destroyed.

The following illustrates the sequence of events. Let's just define a few placeholder classes and functions:

void messagebox(const char*) {
    cout << "messagebox()" << endl;
}

struct tmp {
    tmp(const char* content) : content(content) { cout << "tmp c'tor" << endl; }
    ~tmp() { cout << "tmp d'tor" << endl; }
    const char* c_str() { return content.c_str(); }
private:
    string content;
};

struct ss {
    tmp str() { return tmp("test"); }
};

With this in place, your first version

ss s;
const char* name = s.str().c_str();
messagebox(name);

produces the following output:

tmp c'tor
tmp d'tor
messagebox()

Whereas the second version

ss s;
messagebox(s.str().c_str());

changes the sequence in the output:

tmp c'tor
messagebox()
tmp d'tor

(Live sample code)

IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • Thank you for your answer. Any ideas why debugger do not show variable "name" at all? [Visual Studio 2010] – Arrekin Sep 14 '16 at 05:50
  • @Arrekin: In the first code snippet `name` should be displayed under both the *Locals* and *Autos* tab. If it doesn't show, I don't know why. In a debug build it should point to a string with the contents `0xCD`, marking freed memory. In case you need to inspect variables that aren't displayed automatically, you can always add a [watch](https://msdn.microsoft.com/en-us/library/0taedcee.aspx). – IInspectable Sep 14 '16 at 14:56