Strange behavior with Standard C++ 'string' objects

G

Guest

Hello,

I'm new to the boards, and I've been struggling with a problem porting my
company's code from Visual C++ 6.0 to Visual C++ 2005.

We've found some crashes that I've traced to the 'c_str()' member of the
standard string class. I've debugged into the deepest layers of the system
code and cannot explain this.

What we have is a class with a private 'string' member:

string _linkType;

And public function that returns it:

class Link
....
string getLinkType() const {return _linkType; }

When attempting to access the string as a char * later in the code, the
resulting string "1M" does not get extracted properly:

Link *ilink;
const char *pLinkType;
pLinkType = ilink->getLinkType().c_str();

The result of this is that pLinkType is "".

Now - here's the weird part.

const char *blah;
string strvalue;
strvalue = ilink->getLinkType();
blah = strvalue.c_str();

If I do this, 'blah' contains the proper string '1M'.

How can this be? If I break apart the operation into two lines, it works.
Keep it as one line and it fails!?

I've tried defining the member as std::string and return value from the get
function as std::string as well, with no change in behavior. I'd really
like a good solution to this, as this example literally shows up hundreds of
times in our code. It would be a lot of work to make the massive changes to
ensure the proper value is getting returned.

This works just fine in Visual C++ 6.0. I've even installed VC++2005 SP1 to
see if it was fixed there, with no change.

Thanks so much in advance for your help!
 
B

Bo Persson

Brian said:
Hello,

I'm new to the boards, and I've been struggling with a problem
porting my company's code from Visual C++ 6.0 to Visual C++ 2005.

We've found some crashes that I've traced to the 'c_str()' member of
the standard string class. I've debugged into the deepest layers of
the system code and cannot explain this.

What we have is a class with a private 'string' member:

string _linkType;

And public function that returns it:

class Link
....
string getLinkType() const {return _linkType; }

When attempting to access the string as a char * later in the code,
the resulting string "1M" does not get extracted properly:

Link *ilink;
const char *pLinkType;
pLinkType = ilink->getLinkType().c_str();

The result of this is that pLinkType is "".

This produces a pointer into a temporary return value, which disappears at
the semi-colon.
Now - here's the weird part.

const char *blah;
string strvalue;
strvalue = ilink->getLinkType();
blah = strvalue.c_str();

If I do this, 'blah' contains the proper string '1M'.

Because "1M" is saved inside strvalue, and blah points to that value. blah
is valid as long at strvalue doesn't change.
This works just fine in Visual C++ 6.0.

No it didn't. It just seemed to work. :)
I've even installed VC++2005
SP1 to see if it was fixed there, with no change.

That's a good idea anyway.



Bo Persson
 
T

Tamas Demjen

Brian said:
Link *ilink;
const char *pLinkType;
pLinkType = ilink->getLinkType().c_str();

The result of this is that pLinkType is "".

What happens here is that getLinkType() returns a temporary object.
You're not storing it anywhere, so it gets destructed as soon as the
assignment is executed. c_str() is getting a pointer to a member of this
temporary string. When the string returned by getLinkType() goes out of
scope, the const char* returned by c_str() gets invalidated too.
const char *blah;
string strvalue;
strvalue = ilink->getLinkType();
blah = strvalue.c_str();

If I do this, 'blah' contains the proper string '1M'.

That works as expected, since the string returned by getLinkType is no
longer a temporary. You have a local copy of it, so blah will be valid
as long as strvalue is alive. However, as soon as strvalue goes out of
scope, the value of blah will become undefined.
How can this be? If I break apart the operation into two lines, it works.
Keep it as one line and it fails!?

It's not a matter of having 1 line or 2 lines, it has to do with the
temporary. You have to remember that unless you declare a variable and
give a name to it, it's nothing more than a temporary that dies at the
end of the instruction (when the semicolon is closed). Getting a pointer
to a temporary, or one of its members, is pretty dagerous an unpredictable.

In VC6 the compiler didn't immediately destroy your temporary, so your
pointer was still alive. However, there's no such guarantee. You were
exploiting undefined behavior, and with VC++ 2005 your luck has turned
around. In fact, you should be happy that you caught a bug, because it
was always hanging in the air, waiting for an accident to happen.

Tom
 
G

Guest

Thank you both so much for your help. What you're saying makes complete
sense. I guess that fact that it worked in 6.0 led me to believe a change in
behavior. Bottom line is, we got lucky and our luck ran out.

Thanks again!
 
N

Nathan Mates

Thank you both so much for your help. What you're saying makes
complete sense. I guess that fact that it worked in 6.0 led me to
believe a change in behavior. Bottom line is, we got lucky and our
luck ran out.

I think you're going to find that 2005 is a real nitpicker when it
comes to sketchy code. I much prefer things to fail instantly and
obviously, rather than being lucky.

Nathan Mates
 
B

Ben Voigt

Brian Kunz said:
Thank you both so much for your help. What you're saying makes complete
sense. I guess that fact that it worked in 6.0 led me to believe a change
in
behavior. Bottom line is, we got lucky and our luck ran out.

This is the fix you are probably looking for, that won't require a million
changes to scattered code:

class Link
....
const std::string& getLinkType() const {return _linkType; }

pLinkType = ilink->getLinkType().c_str(); // now OK
 
G

Guest

Ben Voigt said:
This is the fix you are probably looking for, that won't require a million
changes to scattered code:

class Link
....
const std::string& getLinkType() const {return _linkType; }

pLinkType = ilink->getLinkType().c_str(); // now OK
YES YES! Perfect! That works wonderfully, thank you so much.
 
B

Ben Voigt

Brian Kunz said:
YES YES! Perfect! That works wonderfully, thank you so much.

Note that the string object is now the actual member variable, so the
lifetime is now equal to the lifetime of the object ilink refers to. That's
probably long enough for all callers, but it might not always be.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top