17.6.13

On casting the result of malloc()

It my be that a good way to drive traffic to your (relatively) new blog would be to find a contentious but ultimately minor technical argument and take sides, and so without further ado:
uint8_t *p = (uint8_t*)malloc(n);
There's a large amount of debate around casting the result of malloc(), and we're going to examine whether or not it's necessary. (The short answer is that it isn't, but we will explore why in more detail.) There are three main scenarios in which a cast of malloc() could be used, either in C or in C++, or in what we'll refer to as "C/C++" (a misguided attempt to write in both languages at once).

In C

This is fairly straightforward - the C standard (as of C89, at least) specifies that any void pointer can be implicitly converted to any other pointer type, so any cast would be unnecessary, so therefore we shouldn't add unnecessary casts to our code because casts are bad, so we should write the following:
uint8_t *p = malloc(n);
We can go slightly further than this if we want to allocate an instance of a specific type, rather than a buffer of arbitrary size, and we should phrase the call to malloc() thus:
Type *p = malloc(sizeof(*p));
In this case, the compiler can calculate the size we want to allocate for the object from the dereferenced pointer type. Some people would have you phrase that as:
Type *p = (Type*)malloc(sizeof(Type));
Which manages to be ugly, repetitive and fragile, mentioning the name of Type three times, where once would suffice. We should not listen to these people.
Another argument against casting in C is that if you've neglected to #include <stdlib.h>, then you would get a warning about a cast from int to a pointer type. This would be due to the compiler assuming that malloc() returns an int as it hasn't seen a prototype. This is technically true, but I would think that if you've neglected to include system header files, you'd have to be very unlucky if the worst outcome was getting a single warning (i.e. you will most likely have larger problems). And it seems that recent versions of GCC will give you a warning ("incompatible implicit declaration of built-in function ‘malloc’") if <stdlib.h> is missing, whether you cast the result of malloc() or not.

In C++

The argument in C++ is also fairly straightforward—while implicit casts of void pointers are verboten, there is really no need to use malloc() at all in C++, where new[] exists, and is much more typesafe:
uint8_t *p = new uint8_t[n];
And in the case of allocating an instance of a type we could write something like this (which will also allow you to pass arguments to the Type constructor):
Type *p = new Type(a, b, c);
In some limited circumstances, you may want to allocate memory for an object in an an unusual way, but you can still use a placement new on a void pointer in this kind of situation:
void *p = memalign(64, sizeof(Type));
Type *t = new(p) Type(a, b, c); // no casting required

In "C/C++"

One remaining argument which might be raised is that you'd like to write code which can be compiled with both a C compiler and a C++ compiler (simultaneously, perhaps?). In this case, people will try to convince you that you'd need to use malloc() for C compatibility, and you'll need to cast its result for C++ compatibility, so in this specific case, you really have no choice but to write:
uint8_t *p = (uint8_t*)malloc(n);
And these people are wrong, for two reasons. Firstly, if I genuinely need code which compiles to both languages, I'm going to use the preprocessor so I can work with the union of the idioms of both languages, rather than the intersection:
#ifdef __cplusplus
#define MY_MALLOC(type_, size_) static_cast<type_>(malloc(size_)) // ...or even "new type_[size_]"
#else//__cplusplus
#define MY_MALLOC(type_, size_) malloc(size_)
#endif//__cplusplus

//later...
uint8_t *p = MY_MALLOC(uint8_t, n);
But (secondly) there are very few reasons to do this kind of thing anyway - if you have some C code, just compile it with a C compiler and link it against your C++ application, possibly with some judicious use of extern "C" here and there.
So, in summary, there are no situations where it is necessary to cast the result of malloc()—it is at best redundant, and at worst actively detrimental to your code's quality.