Is it legal to cast this way?
void probability(void **value)
{
double v = 0.1234;
*value = (void *) *(uint64_t *) &v;
}
I know, this is a bad thing, but I’m 100% sure that sizeof(double) = sizeof(void *) = sizeof(uint64_t) on target machine.
It’s undefined behavior, because your code violates the strict aliasing rules.
The compiler is permitted to assume that pointers to most unrelated types don’t point to the same memory. You create a
uint64_t *(the result of the cast) that points to memory which is “actually” a double, and you expect reading from that pointer to give you a value that has something to do with the double.The purpose of the strict aliasing rules is to allow the compiler to make various optimizations that will break this code – the most likely is that the compiler can “deduce” that
vis unused and never initialize it, since it’s never accessed via any valid name or pointer, only invalid aliases.I haven’t checked with this specific code, but GCC does in fact rely on strict aliasing at high optimization, and will break this sort of code.
The way to fix strict aliasing problems is with
memcpy:Once this is done, or if you’re using a compiler that doesn’t rely on strict aliasing, or for which that reliance can be avoided (
--no-strict-aliasing), then you still have a problem. The standard doesn’t actually guarantee that every numeric value of the same size as an address, can actually be represented invoid*. For example, it’s legal for the implementation to (in effect) have padding bits in pointers, and to crash if you try to create a pointer value with the wrong values in the padding bits. In practice that isn’t going to happen on any hardware that you’d call “normal”, but nevertheless the standard doesn’t permit your code.