Scanning the legacy "C" code, often I find code snippets similar to the following code:
#include <stdio.h>
static const char msg_buf [1024];
static void log (const char* msg) { /* log the message */ }
/* ... some place later */
int i = 43;
const char* msg = "Safety first"
sprintf (msg_buf, "%s : %d\n", msg, i);
log (msg_buf);
Any static code analysis tool such as (
cppcheck,
flawfinder,
RATS, etc.) will mark call to
sprintf() as unsafe on the ground that it doesn't check for buffer overflow.
However, if you can compile your program with C++ compiler, the easiest fix to such a problem would be to use
ostringstream C++ standard library string buffer instead:
#include <sstream>
static void log (const char* msg)
{
/* log the message */
}
/* Add a wrapper version of log() function
* that accepts stream string as an argument
* and clears up buffer after the message
* has been logged.
*/
static void log (const std::ostringstream& msg)
{
log (msg.str().c_str());
msg.str ("");
}
namespace {
std::ostringstream msg_buf;
}
/* ... some place later */
int i = 43;
const char* msg = "Safety first."
msg_buf << msg << " " << i << std::endl;
log (msg_buf); /* overloaded */
Note that we added a wrapper log() function to help us hide the syntax and to clean up the buffer after the message has been logged.
Formatting Options
It takes time to get used to C++ stream formatting.
For example, the following code snippet prints an integer number as upper case hex to the buffer:
#include <stdio.h>
int i = 43;
char msg_buf[1024];
snprintf (msg_buf, 1024, "Print hex number : %0X.", i);
To achieve equivalent result with C++ stream, you need to insert a number of stream manipulators first.
std::hex manipulator specifies that integers should be treated as hexadecimal (base 16) values.
std::showbase manipulator specifies that the base of a number is to be output ahead of the number (a leading
0 for
octals; a leading
0x or
0X for
hexadecimals). This setting is reset with stream manipulator
noshowbase.
std::setfill (
char) manipulator pads empty space of the justified field with
char character. By default, the filler is a whitespace.
std::uppercase manipulator converts the stream to upper case.
std::width (
d) manipulator specifies the minimum widht of the data field.
#include <sstream>
#include <iomanip>
int i = 43;
std::ostringstream msg_buf;
msg_buf << "Hex number : "
<< std::hex << std::showbase
<< std::setfill('0') << std::uppercase
<< i << ".";
For example, to print a date in YYYYDDMM format, the "C" code might look like this:
#include <stdio.h>
int year = 2011;
int day = 14;
int month = 1;
char msg_buf[1024];
snprintf (msg_buf, 1024,
"Today's date: %04d%02d%02d",
year, day, month);
We can easily rework the solution in a safe C++ manner:
#include <sstream>
#include <iomanip>
int year = 2011;
int day = 14;
int month = 1;
std::ostringstream msg_buf;
msg_buf << std::setw(4)
<< year
<< std::setw(2) << std::setfill('0')
<< day
<< std::setw(2) << std::setfill('0')
<< month;