#include <iostream>
int main(int argc, char * argv[])
{
int a = 0x3f800000;
std::cout << a << std::endl;
static_assert(sizeof(float) == sizeof(int), "Oops");
float f2 = *reinterpret_cast<float *>(&a);
std::cout << f2 << std::endl;
void * p = &a;
float * pf = static_cast<float *>(p);
float f3 = *pf;
std::cout << f3 << std::endl;
float f4 = *static_cast<float *>(static_cast<void *>(&a));
std::cout << f4 << std::endl;
}
I get the following info out of my trusty compiler:
me@Mint-VM ~/projects $ g++-5.3.0 -std=c++11 -o pun pun.cpp -fstrict-aliasing -Wall
pun.cpp: In function ‘int main(int, char**)’:
pun.cpp:11:45: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
float f2 = *reinterpret_cast<float *>(&a);
^
pun.cpp:21:61: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
float f4 = *static_cast<float *>(static_cast<void *>(&a));
^
me@Mint-VM ~/projects $ ./pun
1065353216
1
1
1
me@Mint-VM ~/projects $ g++-5.3.0 --version
g++-5.3.0 (GCC) 5.3.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
I don't really understand when and why I get type-punned errors in some places and not in others.
So, strict aliasing:
Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias each other.)
Line 11 claims I'm breaking strict-aliasing. I don't see a case where this can possibly hurt anything - the pointer "comes into existence", is immediately dereferenced, then thrown away. In all likelyhood, this will compile down to zero instructions. This seems like absolutely no risk - I'm telling the compiler EXACTLY what I want.
Lines 15-16 proceed to NOT elicit a warning, even though the pointers to the same memory location are now here to stay. This appears to be a bug in gcc.
Line 21 elicits the warning, showing that this is NOT limited to just reinterpret_cast.
Unions are no better (emphasis mine):
...it's undefined behavior to read from the member of the union that wasn't most recently written. Many compilers implement, as a non-standard language extension, the ability to read inactive members of a union.
This link talks about using memcpy, but that seems to just hide what you're really trying to accomplish.
For some systems, it is a required operation to write a pointer to an int register, or receive an incoming byte stream and assemble those bytes into a float, or other non-integral type.
What is the correct, standard-conforming way of doing this?
Use memcpy
:
memcpy(&f2, &a, sizeof(float));
If you are worried about type safety and semantics, you can easily write a wrapper:
void convert(float& x, int a) {
memcpy(&x, &a, sizeof(float));
}
And if you want, you can make this wrapper template to satisfy your needs.