I put in the cast simply to show disapproval of the ugly hole in the type system, which allows code such as the following snippet to compile without diagnostics, even though no casts are used to bring about the bad conversion:
double d;
void *p = &d;
int *q = p;
I wish that didn't exist (and it doesn't in C++) and so I cast. It represents my taste, and my programming politics. I'm not only casting a pointer, but effectively, casting a ballot, and casting out demons of stupidity. If I can't actually cast out stupidity, then at least let me express the wish to do so with a gesture of protest.
In fact, a good practice is to wrap malloc
(and friends) with functions that return unsigned char *
, and basically never to use void *
in your code. If you need a generic pointer-to-any-object, use a char *
or unsigned char *
, and have casts in both directions. The one relaxation that can be indulged, perhaps, is using functions like memset
and memcpy
without casts.
On the topic of casting and C++ compatibility, if you write your code so that it compiles as both C and C++ (in which case you have to cast the return value of malloc
when assigning it to something other than void *
), you can do a very helpful thing for yourself: you can use macros for casting which translate to C++ style casts when compiling as C++, but reduce to a C cast when compiling as C:
/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif
If you adhere to these macros, then a simple grep
search of your code base for these identifiers will show you where all your casts are, so you can review whether any of them are incorrect.
Then, going forward, if you regularly compile the code with C++, it will enforce the use of an appropriate cast. For instance, if you use strip_qual
just to remove a const
or volatile
, but the program changes in such a way that a type conversion is now involved, you will get a diagnostic, and you will have to use a combination of casts to get the desired conversion.
To help you adhere to these macros, the the GNU C++ (not C!) compiler has a beautiful feature: an optional diagnostic which is produced for all occurrences of C style casts.
-Wold-style-cast (C++ and Objective-C++ only)
Warn if an old-style (C-style) cast to a non-void type is used
within a C++ program. The new-style casts (dynamic_cast,
static_cast, reinterpret_cast, and const_cast) are less vulnerable
to unintended effects and much easier to search for.
If your C code compiles as C++, you can use this -Wold-style-cast
option to find out all occurrences of the (type)
casting syntax that may creep into the code, and follow up on these diagnostics by replacing it with an appropriate choice from among the above macros (or a combination, if necessary).
This treatment of conversions is the single largest standalone technical justification for working in a "Clean C": the combined C and C++ dialect, which in turn technically justifies casting the return value of malloc
.