For example, Standard C declares
void *malloc(size_t size); void free(void *p);
so that for any object type
T *p; ... p = malloc(sizeof(T)); ... free(p);
should compile in C without complaint. No casting needed.
An unfortunate consequence of C's pointer conversion rules is that they permit accidental conversions between unrelated pointer types. For example:
double *pd; long *pl; void *pv; ... pv = pd; // compiles in C ... pl = pv; // compiles in C
pl is declared to point to a
long, it now points to a
*pl would produce undefined behavior.
C++ reduces the chances of such mishaps by applying slightly more restrictive conversion rules: For any object type
T, a C++ program can convert a
T * to a
void *, but not a
void * to a
T *. Here's the rationale.
T * to
void * is generally safe because the program can do little harm with a
void * object. For example, if
pv has type
void *, compilers reject attempts to access the object to which
pv points. Expressions such as
pv simply don't compile.
Although converting a pointer such as
void * to
T * (for any non-
void T) is itself safe, it throws the door wide open for future unsafe operations, such as accidentally converting an object into something that it really isn't.
Thus, for any (non-
void) object type
T, C++ requires a cast when converting from
void * to
T *, as in:
pv = pd; // compiles in C and C++ ... pl = pv; // compiles in C, but not in C++ pl = (long *)pv; // compiles in C and C++
Using a cast enables the code to compile, but it doesn't eliminate the undefined behavior. In this case, the cast really is an indicator that the operation is unsafe. It's another example of how C++ requires things to be more explicit and locked down than in its forebear. In future articles, I'll discuss how other keywords have slightly different meanings in the two languages.
Dan Saks is a teacher and writer on C++. This is a modified version of an article that originally appeared in EE Times in June 2008.