Search code examples
c++strict-aliasingtype-punning

How do I reinterpret data through a different type? (type punning confusion)


#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?


Solution

  • 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.