I recently stumbled upon a subtle bug in a benchmark code which again reminds me to never use C++ again, if I can.
Here’s a buggy snippet from this code (simplified):
// BUGGY
ostringstream os;
int i = 1;
os << "foo-" << i << ".dat";
const char *filename = os.str().c_str();
int fd = open(filename, O_RDONLY);
You may expect above code to try open a file named foo-1.dat
but that’s not what is happening here.
In this snippet, os.str()
create a temporary string
object which is destroyed immediately after call to c_str()
method. So, filename
ends up pointing to freed memory which can of course contain arbitrary content (till you reach a NULL
).
This fix would be to first assign result of os.str()
to a string object explicitly which would have lifetime of the current scope:
// FIXED
ostringstream os;
int i = 1;
os << "foo-" << i << ".dat";
string s = os.str();
const char *filename = s.c_str();
int fd = open(filename, O_RDONLY);
Such bugs would be much harder to catch and fix in larger programs with complicated abstractions, typical of C++ programs.
Fortunately, we now have alternative languages like Rust which can detect such errors at compile time while giving a level of control and performance equivalent to C/C++.