The first time you try compiling your C code with a C++ compiler can be terrifying. One of the most troublesome offenders is malloc, and you will have your eyes opened to the number of implicit type conversions in your code.
Table of Contents:
malloc: C vs C++- C++ Casts
- C-style Casts
- Casting Recommendations
- Updating Our
mallocCalls - Further Reading
malloc: C vs C++
Consider the following:
int * p = malloc(10);
Recall that malloc returns a void *. The C compiler will happily convert the void * result into whatever type you require. Lines of code like this are likely scattered throughout your C projects.
The C++ compiler is not as kind. Unlike C, the C++ compiler allows implicit conversions TO a void * type, but to convert FROM a void * type requires an explicit cast.
error: cannot initialize a variable of type 'int *' with an rvalue
of type 'void *'
int * p = malloc(10);
^ ~~~~~~~~~~
1 error generated.
So what can we do about this?
The first thing that comes to your mind is our friend, the C-style cast:
int * p = (int*)malloc(10);
This will work, but this style of cast is not recommended in C++. There are more explicit methods which allow us to describe the intention of our cast.
C++ Casts
C++ provides a variety of ways to cast between types:
static_castreinterpret_castconst_castdynamic_cast- C-style casts
Each of the C++ casts has the following generic form:
cast_name<cast_to_type>(item_to_cast)
Let’s look at what each of these casts do.
static_cast
static_cast is the main workhorse in our C++ casting world. static_cast handles implicit conversions between types (e.g. integral type conversion, any pointer type to void*). static_cast can also call explicit conversion functions.
int * y = static_cast<int*>(malloc(10));
We will primarily use it for converting in places where implicit conversions fail, such as malloc.
There are no runtime checks performed for static_cast conversions.
static_cast cannot cast away const or volatile.
reinterpret_cast
reinterpret_cast is a compiler directive which tells the compiler to treat the current type as a new type.
You can use reinterpret_cast to cast any pointer or integral type to any other pointer or integral type. This can lead to dangerous situations: nothing will stop you from converting an int to a std::string *.
You will use reinterpret_cast in your embedded systems. A common scenario where reinterpret_cast applies is converting between uintptr_t and an actual pointer or between:
error: static_cast from 'int *' to 'uintptr_t'
(aka 'unsigned long') is not allowed
uintptr_t ptr = static_cast<uintptr_t>(p);
^~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
Instead, use this:
uintptr_t ptr = reinterpret_cast<uintptr_t>(p);
reinterpret_cast cannot cast away const or volatile.
const_cast
const_cast adds or removes const from a variable. Strangely enough, you can also use const_cast to add or remove volatile from a variable. No other C++ cast can add or remove these keywords.
Use this cast carefully. If you declared the original variable as const, it’s not safe to remove const and start modifying the underlying data.
You should primarily use const_cast to add const to a variable (such as for a function overload to use a const version). If you need to remove const from a variable, I recommend stopping and thinking about why you are in this situation.
const_cast and volatile
Removing volatile from a keyword is definitely more common in embedded systems than removing const. If you decide to use the volatile variable as a function parameter, you will need to remove the keyword.
Consider the following contrived example:
void test(int * x)
{
//do something
}
int main(void)
{
volatile int p = 0;
test(&p);
return 0;
}
In C, this example would compile. C++ throws an error:
test.c:14:2: error: no matching function for call to 'test'
test(&p);
^~~~
test.c:5:6: note: candidate function not viable: 1st argument ('volatile int *')
would lose volatile qualifier
void test(int * x)
^
1 error generated.
To prevent this error, we will need to use const_cast:
test(const_cast<int*>(&p));
dynamic_cast
I have never used dynamic_cast in my embedded projects, and I usually keep run-time-type-information (RTTI) disabled. I will provide a cursory overview, but please see “Further Reading” below for more detailed information.
We use dynamic_cast to handle polymorphism. dynamic_cast can convert pointers and references to any polymorphic type at run-time, primarily to cast down a type’s inheritance hierarchy. If dynamic_cast can’t find the desired type in the inheritance hierarchy, it will return nullptr for pointers or throw a std::bad_cast exception for references.
C-style Casts
A C-style cast in C++ tries the following casts in order, using the first C++ cast that works:
const_caststatic_caststatic_cast, thenconst_cast(change type + remove const)reinterpret_castreinterpret_cast, thenconst_cast(change type + remove const)
Note that dynamic_cast is never considered when using a C-style cast.
Casting Recommendations
Five casts is a lot to keep in mind. Here are some quick rules of thumb for these new casts:
- Use
static_castfor your ordinary conversions - Use
reinterpret_castfor specific cases were you need to reinterpret underlying data (e.g. converting from a pointer touintptr_t) - Use
dynamic_castfor converting pointers and references along an inheritance hierarchy - Only use
dynamic_caston classes with virtual members - Use
const_castwhen you need to removeconstorvolatilekeywords. Think carefully before using this cast. - Avoid C-style casts. Be explicit in your intentions.
One unappreciated benefit of using C++-style casts is you can search through your code and immediately locate all casts. With C-style casts, you cannot easily tell the difference between a declaration and a cast if you are performing a search.
Updating Our malloc Calls
That was quite the long detour!
Coming back to our malloc example, we can now see that we should use a static_cast:
int * p = static_cast<int*>(malloc(10));
All that casting really is a nuisance, isn’t it?
Further Reading
If you find C++ casting still to be unclear, try the following two links:
These are useful references if you need to refer to API documentation:
- operator
new - cppreference: explicit casts
static_castconversionreinterpret_castconversionconst_castconversiondynamic_castconversion
Using C++ Without the Heap
Want to use C++, but worried about how much it relies on dynamic memory allocations? Our course provides a hands-on approach for learning a diverse set of patterns, tools, and techniques for writing C++ code that never uses the heap.
Learn More on the Course Page

Why not just use new instead of malloc() ?
Hi Artsiom,
Agreed that for new code you should definitely use new. This article is written from the perspective of migrating from C to C++ and explaining why existing C code calling malloc() will no longer compile in C++.
Please note that using malloc on anything other than primitive types (eg stuff with a constructor) and proceeding to use them is UB, one will need to use the placement new first to start the object’s lifetime.