Discussion:
c_str()
(too old to reply)
Ron Kochanowicz
2005-10-13 07:22:40 UTC
Permalink
Hello,
I have a TEdit control on my form called CWID.
Upon a button event, I do the following:

char* CWIDPtr = CWID->Text.c_str();

I understand c_str() to return a null terminated char array.

However, I'm finding something very strange going on. If CWID->Text =
"BIGTIME", what is returned is actually to the CWIDPtr is "BIGT\x14".

During run-time if I break on the actual line char *CWIDPtr =
CWID->Text.c_str() and then single step through the operation in "dstring.h"
I get returned the correct null terminated array: "BIGTIME"

I'm at a loss. Any idea what's going on and why the c_str() routine only
returns the correct value when I single step through the c_str() construct??

TIA,
Ron
Richard Bradbrook
2005-10-13 08:08:31 UTC
Permalink
I'm pretty sure the help says the pointer returned by c_str is only valid
for the call it's used in.

i.e. you can't assign its return value to another variable and then carry on
using that variable.

You'd need to take a local copy of the string via the standard StrCopy
function etc.
Andrue Cope [TeamB]
2005-10-13 08:37:55 UTC
Permalink
Post by Richard Bradbrook
I'm pretty sure the help says the pointer returned by c_str is only
valid for the call it's used in.
That's a safe and sensible assumption but in truth it will only change
when the AnsiString changes. In general C++ terms though c_str() is a
horror. Experience of AnsiString allows you to spot how reliable the
pointer is but generally speaking methods that return private data are
not to be trusted.

It's also worth noting that the pointer itself might not change. It
depends how AnsiString is implemented but if it's efficient then
deleting characters will probably just shuffle the characters in the
existing buffer rather than perform a de/reallocation.

FWIW I nearly shot myself in the foot earlier this week with this code

StringType ClassType::Str();

char const * fred=object.Str().c_str();

This was in a DLL API and 'fred' in this example was provided by the
calling EXE. If things worth mucking up it's worth mucking up
completely :)

Luckily I caught that during the code review phase and was able to
change the return value to 'StringType const &'. I still wasn't happy
though. As it was my string class I decided to require the caller to
specify how the buffer was to be allocated. They now have the choice of
private member data or static round-robin list with 1000 strings.

It makes using c_str() a PITA but that's probably how it should be.
--
Andrue Cope [TeamB]
[Bicester, Uk]
http://info.borland.com/newsgroups/guide.html
Chris Uzdavinis (TeamB)
2005-10-13 13:30:09 UTC
Permalink
Post by Richard Bradbrook
I'm pretty sure the help says the pointer returned by c_str is only valid
for the call it's used in.
It's valid for the lifetime of the buffer into which it points, which
is the lesser of A) the lifetime of the AnsiString object that
produced it, or B) the window of time in which no mutating functions
are called on the AnsiString.

If the String changes or is destroyed, the pointer is invalidated.
Post by Richard Bradbrook
i.e. you can't assign its return value to another variable and then carry on
using that variable.
Well, you can, but in this case it's wrong because Edit->Text returns
a temporary AnsiString, and storing the internal pointer of a
temporary AnsiString object is a bad thing to do, because the
temporary variable lives only as long as the expression in which it's
used.

(So your original statement about it being only valid in the call it's
used in is actually correct, but for a different reason.)
Post by Richard Bradbrook
You'd need to take a local copy of the string via the standard StrCopy
function etc.
You could also make a local copy of the AnsiString and store the
c_str() it returns.
--
Chris (TeamB);
Andrue Cope [TeamB]
2005-10-13 08:11:13 UTC
Permalink
Post by Ron Kochanowicz
char* CWIDPtr = CWID->Text.c_str();
That is potentially dangerous.
Post by Ron Kochanowicz
I understand c_str() to return a null terminated char array.
It returns a pointer to the internal buffer currently being used by the
AnsiString. The above code is dangerous for several reasons:

1.Even if the right hand side is an AnsiString rather than a property
you should not attempt to hang on to the pointer to the buffer. If the
AnsiString changes your pointer is highly likely to become invalid.

2.Text is a property and this could mean that you are calling c_str()
on a temporary. IOW an AnsiString is created for that expression but
destroyed as soon as the expression evaluation (your assignment) is
complete.

3.The lhs should in any case be declared as 'char const *' to prevent
accidental writing back to the string.
Post by Ron Kochanowicz
However, I'm finding something very strange going on. If CWID->Text =
"BIGTIME", what is returned is actually to the CWIDPtr is "BIGT\x14".
Don't do what you're doing. Use strcpy() to make your own copy of the
string:

strcpy( buffer, CWID->Text.c_str() );
or
char * buffer( NewStr( CWID->Text.c_str() ) );

Basically c_str() should be assumed to be a temporary pointer whose
lifetime extends to the end of the expression where it is first used
and no further. In most cases it will last longer than that but it's
better IMO to assume not.
--
Andrue Cope [TeamB]
[Bicester, Uk]
http://info.borland.com/newsgroups/guide.html
Juris
2005-10-13 09:57:38 UTC
Permalink
It's not a good idea to use pointer with c_str() to operate with textual
data. Pointer CWIDPtr points to components internal data structure, that may
change at a time.

I think , that better would be copy data to other temporary structure :

AnsiString CWIDPtrA = CWID->Text;
or
char* CWIDPtr = new char[CWID->Text.Length()+1];
StrCopy(CWIDPtr,CWID->Text.c_str());

===========================================
Some components also may have bugs. For example :
We have TStringList called TEST;

TEST->Text = "My name is :";
TEST->Text += "Juris";

Curiously, but the result will be - "My name is:" insted of "My name
is:Juris";

To avoid this bug - we need to use one Temporary AnsiString Variable.
and then code:
AnsiString whatewer = TEST->Text;
whatewer = "My name is :";
whatewer += "Juris";
Test->Text = whatewer;

will work correctly.
The result will be : "My name is:Juris"
===========================================

Juris.
Post by Ron Kochanowicz
Hello,
I have a TEdit control on my form called CWID.
char* CWIDPtr = CWID->Text.c_str();
I understand c_str() to return a null terminated char array.
However, I'm finding something very strange going on. If CWID->Text =
"BIGTIME", what is returned is actually to the CWIDPtr is "BIGT\x14".
During run-time if I break on the actual line char *CWIDPtr =
CWID->Text.c_str() and then single step through the operation in "dstring.h"
I get returned the correct null terminated array: "BIGTIME"
I'm at a loss. Any idea what's going on and why the c_str() routine only
returns the correct value when I single step through the c_str() construct??
TIA,
Ron
Ron Kochanowicz
2005-10-13 16:22:10 UTC
Permalink
Thank you very much. I was able to resolve the problem by making a local
copy using StrCopy rather than using TEdit->Text directly.

Best Regards,
Ron

Continue reading on narkive:
Loading...