What Is the Lifetime of the Result of Std::String::C_Str()

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.)

Lifetime of returned strings and their .c_str()

someFunctionTakingCStrings(path.string().c_str()); is safe since the standard guarantees that the lifetime of the anonymous temporary path.string() survives the function call. So the pointer returned by c_str() is a valid parameter for someFunctionTakingCStrings.

const std::string path::string() const is safe since, conceptually, you are returning a value copy of tmp, although in practice a compiler will optimise out the value copy (a process called named return value optimisation).

Something like const std::string& path::string() const with the same function body as the one you have would not be defined (since the reference would dangle), and

const char* ub_server()
{
std::string s = "Hello";
return s.c_str();
}

is also undefined, as s is out of scope by the time the function returns.

Finally, note that taking a pointer to an anonymous temporary as a parameter in a function call is not allowed in standard C++ although annoyingly, Visual C++ allows it as an extension.

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

std::string::c_str() and temporaries

The pointer returned by std::string::c_str() points to memory
maintained by the string object. It remains valid until a non-const
function is called on the string object, or the string object is
destructed. The string object you're concerned about is a temporary.
It will be destructed at the end of the full expression, not before and
not after. In your case, the end of the full expression is after the
call to consumer, so your code is safe. It wouldn't be if consumer
saved the pointer somewhere, with the idea of using it later.

The lifetime of temporaries has been strictly defined since C++98.
Before that, it varied, depending on the compiler, and the code you've
written wouldn't have worked with g++ (pre 1995, roughly—g++
changed this almost immediately when the standards committee voted it).
(There wasn't an std::string then either, but the same issues affect
any user written string class.)

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.

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*

Assigning a std::string's c_str() result to that same std::string guaranteed safe by the standard?

In C++17, this was specified as:

basic_string& operator=(const charT* s);

Returns: *this = basic_string(s).

Remarks: Uses traits::length().

This sequence of operations guarantees that a copy is made before the original storage is destroyed. While a standard library is not required to implement this call using this exact sequence of operations, it is required to have the same behavior as described.

In C++20, this wording was reworked, but I would be surprised if the meaning was changed.

Lifetime of the object returned by std::string::substr

It's valid here. The temporary returned by substr is destroyed after the full expression; which includes the initialization of v.

All temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created, and if multiple temporary objects were created, they are destroyed in the order opposite to the order of creation. This is true even if that evaluation ends in throwing an exception.

BTW: This is not assignment but initialization (construction) of v.

about string.c_str() life cycle

From Paragraph 12.2/3 of the C++11 Standard:

[...] Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. This is true even if that evaluation ends in throwing an exception. [...]

This means that the temporary created within the expression that contains the call to func() will live until function call returns.

On the other hand, the lifetime of the temporary in the first code snippet will end up before func() is invoked, and str will be dangling. This will result in Undefined Behavior.



Related Topics



Leave a reply



Submit