In one of my programs, I have to interface with some legacy code that works with const char*
.
Lets say I have a structure which looks like:
struct Foo
{
const char* server;
const char* name;
};
My higher-level application only deals with std::string
, so I thought of using std::string::c_str()
to get back const char*
pointers.
But what is the lifetime of c_str()
?
Can I do something like this without facing undefined behavior ?
{
std::string server = \"my_server\";
std::string name = \"my_name\";
Foo foo;
foo.server = server.c_str();
foo.name = name.c_str();
// We use foo
use_foo(foo);
// Foo is about to be destroyed, before name and server
}
Or am I supposed to immediately copy the result of c_str()
to another place ?
Thank you.
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.)
Technically your code is fine.
BUT you have written in such a way that makes it easy to break for somebody that does not know the code. For c_str() the only safe usage is when you pass it as a parameter to a function. Otherwise you open yourself up-to maintenance problems.
Example 1:
{
std::string server = \"my_server\";
std::string name = \"my_name\";
Foo foo;
foo.server = server.c_str();
foo.name = name.c_str();
//
// Imagine this is a long function
// Now a maintainer can easily come along and see name and server
// and would never expect that these values need to be maintained as
// const values so why not re-use them
name += \"Martin\";
// Oops now its broken.
// We use foo
use_foo(foo);
// Foo is about to be destroyed, before name and server
}
So for maintenance make it obvious:
Better solution:
{
// Now they can\'t be changed.
std::string const server = \"my_server\";
std::string const name = \"my_name\";
Foo foo;
foo.server = server.c_str();
foo.name = name.c_str();
use_foo(foo);
}
But if you have const strings you don\'t actually need them:
{
char const* server = \"my_server\";
char const* name = \"my_name\";
Foo foo;
foo.server = server;
foo.name = name;
use_foo(foo);
}
OK. For some reason you want them as strings:
Why not use them only in the call:
{
std::string server = \"my_server\";
std::string name = \"my_name\";
// guaranteed not to be modified now!!!
use_foo(Foo(server.c_str(), name.c_str());
}
It is valid until one of the following happens to the corresponding string
object:
- the object is destroyed
- the object is modified
You\'re fine with your code unless you modify those string
objects after c_str()
s are copied into foo
but before use_foo()
is called.
Return value of c_str() is valid only until the next call of a nonconstant member function for the same string
The const char*
returned from c_str()
is only valid until the next non-const call to the std::string
object. In this case you\'re fine because your std::string
is still in scope for the lifetime of Foo
and you aren\'t doing any other operations that would change the string while using foo.
As long as the string isn\'t destroyed or modified, using c_str() is OK. If the string is modified using a previously returned c_str() is implementation defined.
For completeness, here\'s a reference and quotation 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()
.