Why does calling c_str() on a function that returns a string not work?
SomeFunction().c_str()
gives you a pointer to a temporary(the automatic variable str
in the body of SomeFunction
). Unlike with references, the lifetime of temporaries isn't extended in this case and you end up with charArray
being a dangling pointer explaining the garbage value you see later on when you try to use charArray
.
On the other hand, when you do
string str_copy = SomeFunction();
str_copy
is a copy of the return value of SomeFunction()
. Calling c_str()
on it now gives you a pointer to valid data.
Why does calling std::string.c_str() on a function that returns a string not work?
getString()
would return a copy ofstr
(getString()
returns by value);
It's right.
thus, the copy of
str
would stay "alive" inmain()
untilmain()
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.
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
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());
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.
c_str() returns empty string
The result of c_str()
is only valid as long as the std::string
object that produced that result is valid.
In your case, AsString()
call produces a temporary std::string
object which is then immediately destroyed. After that the result of that c_str()
call no longer makes any sense. Trying to access the memory pointed by that pointer leads to undefined behavior.
Don't attempt to store the pointer returned by c_str()
. If you need that string as a C-string for an extended period of time, allocate memory buffer for it yourself and copy the result of c_str()
to that buffer.
Another (much better) idea would be not to rush the conversion to C-string. Return the result as std::string
and call c_str()
on it at the very last moment: when you reall really really need a C-string.
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.
My helper function is returning an empty string
il2cppi_to_string()
returns a temporary std::string
, which will be destroyed at the end of the expression that calls il2cppi_to_string()
. You are obtaining a const char*
pointer to the data of that temporary std::string
, which is what ReSharper is warning you about. Since the temporary std::string
is destroyed before the return
, that means getGhostName()
is returning a dangling pointer to invalid memory.
To fix this, change getGhostName()
to return a std::string
instead of a const char*
:
std::string getGhostName(GhostAI* ghostAI)
{
if (ghostAI) {
GhostInfo* ghostInfo = getGhostInfo(ghostAI);
const auto ghostName = ghostInfo->fields.u0A6Du0A67u0A74u0A71u0A71u0A66u0A65u0A68u0A74u0A6Au0A6F.u0A65u0A66u0A6Eu0A67u0A69u0A74u0A69u0A65u0A74u0A6Fu0A67;
return il2cppi_to_string(ghostName);
}
return "UNKNOWN";
}
Why does string::c_str() return a const char* when strings are allocated dynamically?
In C and C++, const
translates more or less to "read only".
So, when something returns a char const *
, that doesn't necessarily mean the data it's pointing at is actually const
--it just means that the pointer you're receiving only supports reading, not writing, the data it points at.
The string object itself may be able to modify that data--but (at least via the pointer you're receiving) you're not allowed to modify the data directly.
Related Topics
Getting Size of Array from Pointer C++
What Is the Partial Ordering Procedure in Template Deduction
What's the Difference Between Std::Move and Std::Forward
How to Append Text to a Text File in C++
Is It a Conforming Compiler Extension to Treat Non-Constexpr Standard Library Functions as Constexpr
Is the Return Type Part of the Function Signature
What Is the Efficient Way to Count Set Bits At a Position or Lower
Generating Combinations in C++
Is the Safe-Bool Idiom Obsolete in C++11
Why Is the Template Argument Deduction Not Working Here
What Requirements Must Std::Map Key Classes Meet to Be Valid Keys
How to Properly Use Namespaces in C++
Programmatically Create Static Arrays At Compile Time in C++