How to Return a Std::String.C_Str()

Is it safe to call c_str() directly on returned std::string from a function?

It is safe in the given example as the lifetime of the returned string ends at the end of the statement containing the function call.

In general I recommend reading https://en.cppreference.com/w/cpp/language/lifetime

Why does calling std::string.c_str() on a function that returns a string not work?

getString() would return a copy of str (getString() returns by value);

It's right.

thus, the copy of str would stay "alive" in main() until main() returns.

No, the returned copy is a temporary std::string, which will be destroyed at the end of the statement in which it was created, i.e. before std::cout << cStr << std::endl;. Then cStr becomes dangled, dereference on it leads to UB, anything is possible.

You can copy the returned temporary to a named variable, or bind it to a const lvalue-reference or rvalue-reference (the lifetime of the temporary will be extended until the reference goes out of scope). Such as:

std::string s1 = getString();    // s1 will be copy initialized from the temporary
const char* cStr1 = s1.c_str();
std::cout << cStr1 << std::endl; // safe

const std::string& s2 = getString(); // lifetime of temporary will be extended when bound to a const lvalue-reference
const char* cStr2 = s2.c_str();
std::cout << cStr2 << std::endl; // safe

std::string&& s3 = getString(); // similar with above
const char* cStr3 = s3.c_str();
std::cout << cStr3 << std::endl; // safe

Or use the pointer before the temporary gets destroyed. e.g.

std::cout << getString().c_str() << std::endl;  // temporary gets destroyed after the full expression

Here is an explanation from [The.C++.Programming.Language.Special.Edition] 10.4.10 Temporary Objects [class.temp]]:

Unless bound to a reference or used to initialize a named object, a
temporary object is destroyed at the end of the full expression in
which it was created. A full expression is an expression that is
not a subexpression of some other expression.

The standard string class has a member function c_str() that
returns a C-style, zero-terminated array of characters (§3.5.1, §20.4.1). Also, the operator + is defined to mean string concatenation.
These are very useful facilities for strings . However, in combination they can cause obscure problems.
For example:

void f(string& s1, string& s2, string& s3)
{

const char* cs = (s1 + s2).c_str();
cout << cs ;
if (strlen(cs=(s2+s3).c_str())<8 && cs[0]==´a´) {
// cs used here
}

}

Probably, your first reaction is "but don’t do that," and I agree.
However, such code does get written, so it is worth knowing how it is
interpreted.

A temporary object of class string is created to hold s1 + s2 .
Next, a pointer to a C-style string is extracted from that object. Then
– at the end of the expression – the temporary object is deleted. Now,
where was the C-style string allocated? Probably as part of the
temporary object holding s1 + s2 , and that storage is not guaranteed
to exist after that temporary is destroyed. Consequently, cs points
to deallocated storage. The output operation cout << cs might work
as expected, but that would be sheer luck. A compiler can detect and
warn against many variants of this problem.

Inspect the returning value of std::string.c_str()

First Question

The pointer returned by std::c_str() remain valid if the string is not modified. From cppreference.com:

The pointer obtained from c_str() may be invalidated by:

  • Passing a non-const reference to the string to any standard library function, or
  • Calling non-const member functions on the string, excluding operator[], at(), front(), back(), begin(), rbegin(), end() and rend().

In your posted code,

std::string strTest = "This is a test";
const char* s1 = strTest.c_str();
strTest = "This is b tests"; // This line makes the pointer invalid.

and then use of the pointer to access the string is undefined behavior.

std::cout << s1 << std::endl; // Undefined behavior.

After that, it's pointless to try to make sense of what the code does.

Second Question

The standard library provides an operator overload function between std::ostream and char const* so C-style strings can be printed in a sensible way. When you use:

std::cout << "Hello, World.";

you would want to see Hello, World. as output, not the value of the pointer that points to that string.

For reasons beyond the scope of this answer, that function overload is implemented as a non-member function.

template< class CharT, class Traits >
basic_ostream<CharT,Traits>& operator<<( basic_ostream<CharT,Traits>& os,
const CharT* s );

After all the template related tokens are substituted, that line translates to:

std::ostream& operator<<(std::ostream& os, const char* s );

You can see the list of non-member overload functions at cppreference.com.

std::string c_str() scope after returning from function

What would be the lifetime of abc.c_str() pointer returned by StringFunc(), would it be safely copied to variable 'Temp' after StringFunc() returns ?

abc will be valid until StringFunc() function returns. Yes, it's safe to return a copy to CString.

If you return a pointer to std::string::c_str() then it's dangerous, for example:

const char* EvilFunc()  // bad, dont' do it
{
std::string abc = "Hello";
return abc.c_str();
}

const char* p = EvilFunc(); // p becomes wild pointer when EvilFunc returns

CString Temp = StringFunc() is a Shallow copy operation or Deep Copying ?

It's deep copy. it constructs a new CString object from const char*

C++ c_str of std::string returns empty

Presumably the string contains one or more nul characters, resulting in strcpy() not copying all of the bytes. You also have a memory leak from not freeing cstr before you return from the function.

You can avoid both problems by bypassing the unnecessary string array and calling PyBytes_FromStringAndSize:

return PyBytes_FromStringAndSize(string_repr.c_str(), string_repr.size());

Returning 'c_str' from a function

strdup allocates a copy of the string on the heap, which you have to free manually later (with free() I think). If you have the option, it would be much better to return std::string.

The static storage of out doesn't help, because .str() returns a temporary std::string, which is destroyed when the function exits.

What is the lifetime of the result of std::string::c_str()?

The c_str() result becomes invalid if the std::string is destroyed or if a non-const member function of the string is called. So, usually you will want to make a copy of it if you need to keep it around.

In the case of your example, it appears that the results of c_str() are used safely, because the strings are not modified while in that scope. (However, we don't know what use_foo() or ~Foo() might be doing with those values; if they copy the strings elsewhere, then they should do a true copy, and not just copy the char pointers.)

Confusing behavior of C++ string returns and c_str() casts

The result of c_str() is only valid while the string you called it on still exists. In case 3, txtCpy still exists at the point you are writing cout << text. But in Case 1, the string was the return value of sample->getText which is temporary and stop existing at the end of that line .

This issue always will exist if you take pointers or references to other objects. A naked pointer or reference has its own lifetime which may differ from the lifetime of the targeted object. This is unlike Java where object references all participate in the lifetime of the object.

As such, you always need to think about object lifetimes when using these features, and it's commonly recommended to instead use higher level features or other code styles that do not permit lifetime management errors.

You could consider adding a member function to Sample which gets a const char * pointing at the original string (although this is a wee violation of encapsulation, and still has a similar class of problem if you hold onto the pointer and then modify the underlying string). Better would be to just avoid working with the naked pointers entirely.



Related Topics



Leave a reply



Submit