Static casting to/from void*
of another pointer is the same as reinterpret_cast
ing.
Using a pointer to a non-float as a pointer to a float can be UB, including things as innocuous as comparing one pointer value to another.
A hardware reason behind this is that on some platforms, floats have mandatory alignment, and ==
on two float*
may assume that they are pointing to aligned values. On at least one platform, both char*
and void*
take up more memory than a float*
because there is no byte-addressing on the platform; byte addressing is emulated by storing an additional "offset" and doing bitmasking operations under the hood.
On other platforms, different address spaces entirely are used for different types of pointers. One I'm aware of has function and value address spaces being distinct.
The standard, mindful of such quirks and in order to permit certain optimizations, does not fully define what happens when you use incorrectly typed pointers. Implementations are free to document that they behave like a flat memory model that you might expect but are not mandated to.
I cannot remember if this is implementation defined (which means they must document if it is defined or UB) or just UB here (which means compilers are free to define its behavior, but do not have to).
For types that where the T*
is fully valid and proper (ie, there is actually an object of type T
there, we just had a pointer to void*
or char*
to it, or somesuch), then static_cast<T*>(static_cast<void*>(addy)) == reinterpret_cast<T*>(addy)
in every case.
Aliasing issues -- having pointers to the wrong type -- are very tricky.