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.
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";
}
C++ strange behavior with string's c_str() function
What's happening in the invalid case is that GetTheStr() is returning a temporary, then c_str() is returning a reference to its internal data, then the temporary goes out of scope and suddenly you have a dangling reference to storage that is no longer valid. When you assign the returned value of GetTheStr() to a named variable, the variable is still alive and the result of its c_str() is still pointing to valid data.
Lifetimes of temporaries is something that varies between implementations. It is my understanding that a temporary lives for the entire statement (std::cout << GetTheStr().c_str() << endl;
is technically valid to my understanding because the lifteime is required to last for the entire statement, but poorly written because it is relying on a very subtle aspect of lifetime); however, whether a temporary lives beyond that statement to the end of the scope or not is, to my understanding, implementation-defined. I'm probably going to be pilloried for this last paragraph (especially by people with more precise knowledge on the topic), but the short story is that well written code should be more explicit when the lifetime of an object needs to be extended; if you need to retain a reference to the internal data of an object, then it's always best to guarantee that there is a named variable referring to the object to ensure that the containing object's lifetime exceeds the lifetime of the usage of its internal data.
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()
andrend()
.
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.
Related Topics
Why Can't Static_Cast Be Used to Down-Cast When Virtual Inheritance Is Involved
Difference in Performance Between Msvc and Gcc for Highly Optimized Matrix Multplication Code
Why Can't a Derived Class Call Protected Member Function in This Code
Why Is Allocator::Rebind Necessary When We Have Template Template Parameters
Know If .Lib Is Static or Import
How to Read-Write Into/From Text File with Comma Separated Values
C++11 Initializer List Fails - But Only on Lists of Length 2
How to Use Boost Bind with a Member Function
C/C++ Header and Implementation Files: How Do They Work
Checking a Member Exists, Possibly in a Base Class, C++11 Version
Convert String Containing Several Numbers into Integers
How to Best Handle Dynamic Multi-Dimensional Arrays in C/C++
C++ Inlining Class Methods Causes Undefined Reference
Are C++ Enums Signed or Unsigned
How to Correctly and Standardly Compare Floats
Why Catch an Exception as Reference-To-Const