As we discussed in Chapter 1, C++ inherited the C philosophy and its corresponding problems. But that’s not all. It also inherited the standard C library, which is unsafe in several ways, and consequently all its associated problems, sometimes leading to unpredictable behavior up to and including program crashes. For the final chapter in this part of the book, we’ll discuss the possible dangers that await you when you use some of the functions that programmers frequently depend on in these libraries.
When we try to use the C string libraries declared in string.h or functions such as sprintf()
declared in
stdio.h, we may face the following
problems:
The functions that take pointers to character arrays (char *
) crash when given a NULL instead of a
pointer to a valid C string (for example, strlen(NULL)
will
crash).
Some of the functions writing into a buffer might overwrite past the end of the buffer, thus leading to unpredictable application behavior including crashes.
The safer versions of the same functions will not overwrite the buffer, but will stop writing into a buffer just before it ends, thus silently truncating the result—probably not the behavior one would want.
There are several potential ways to address these problems:
Provide versions of the functions that do all the necessary
sanity checks and treat the NULL pointers the same way as they would
handle an empty string (const char*
empty_string = "";
).
For those applications where the speed of these string operations should not be compromised, provide versions with temporary sanity checks that are active only during testing.
However, the best possible solution to this problem is not to use the C string libraries at all. Use the classes provided by C++ instead. For example:
To concatenate two strings, two functions in the C library are
available. strcat()
blindly adds a
string to the end of an existing string in a buffer without ever knowing
where the buffer ends. By contrast, strncat()
adds no more than
the specified number of bytes, which seems like a step in the right
direction, but it still does not know anything about the size of the buffer
it adds to. The programmer is responsible for allocating the right amount of
space and calculating how many bytes to add.
Instead of strcat()
or strncat()
, use
either:
#include <sstream> // ostringstream #include <string> ostringsream buffer; buffer << first_string; buffer << additional_string; string result = buffer.str();
or, even shorter:
#include <string> string result = first_string; result += additional_string;
Not only are these more readable and safer, they are actually faster
for long strings than strcat()
! There are
no buffers to allocate and overwrite.
If you are working with std::string
and provide a
NULL as an argument in a constructor:
std::string empty_string(NULL);
the program does not crash. Instead it throws an exception with a human-readable (well, almost human-readable) explanation of what happened:
basic_string::_S_construct NULL not valid
which translates into plain English as “the constructor of std::string found a NULL as an argument where it expected a valid C string.”
The rule for this chapter to avoid buffer overflows and crashes when using C string library functions is to avoid using C string libraries.
3.16.76.138