Given a simple file loading function,
std::string load_file(const std::string &filename) {
std::ifstream file(filename);
std::string line;
std::stringstream stream;
while (std::getline(file, line)) {
stream << line << "\n";
}
return stream.str();
}
Why does the following print the contents of another_file
twice?
const char *some_file = load_file("some_file").c_str();
const char *another_file = load_file("another_file").c_str();
printf("%s", some_file);
printf("%s", another_file);
The code is broken. You are calling c_str()
on a temporary object that is immediately destroyed. Which means that the values returned by c_str()
are invalid.
You need to make sure that the std::string
objects returned survive at least as long as you hold on to the pointer returned by the call to c_str()
. For example:
std::string some_file = load_file("some_file");
std::string another_file = load_file("another_file");
printf("%s", some_file.c_str());
printf("%s", another_file.c_str());
In a line like this:
const char *some_file = load_file("some_file").c_str();
load_file()
returns a temporary std::string
, and then .c_str()
is called on this temporary.
When the temporary is alive, the pointer returned by .c_str()
points to some meaningful string. But when the temporary "evaporates" (at the semicolon), then that same pointer is pointing to garbage.
The "garbage" may be the same string that the previous call to load_file()
returned, so you have the effect that both raw pointers point to the same string.
But this is just a coincidence.
And your code has a bug.
String classes like std::string
were invented as a convenient way to simplify the C++ programmer's life instead of using raw C string pointers. So, just use std::string
s if you want to safely manage strings in C++.
Consider using .c_str()
just at the boundary with C functions (including printf()
).
So, you can refactor your code like this:
// load_file() returns a std::string, so just keep using std::string.
// Note that returning std::string is efficient thanks to RVO/NRVO
// and C++11 move semantics.
std::string some_file = load_file("some_file");
// Idem for this:
std::string another_file = load_file("another_file");
// Convert from std::string to raw C string pointers at the C boundary
printf("%s\n", some_file.c_str());
printf("%s\n", another_file.c_str());
Even some code like this would work fine:
printf("%s\n", load_file("some_file").c_str());
printf("%s\n", load_file("another_file").c_str());
In fact, note that in this case, even if you are using a temporary (i.e. the strings returned by load_file()
are not copied to named std::string
variables), the temporary is valid during the printf()
call, so the raw pointer returned by .c_str()
points to a valid string while printf()
is doing its printing job.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With