petersohn
petersohn

Reputation: 11730

"dereferencing type-punned pointer will break strict-aliasing rules" warning

I use a code where I cast an enum* to int*. Something like this:

enum foo { ... }
...
foo foobar;
int *pi = reinterpret_cast<int*>(&foobar);

When compiling the code (g++ 4.1.2), I get the following warning message:

dereferencing type-punned pointer will break strict-aliasing rules

I googled this message, and found that it happens only when strict aliasing optimization is on. I have the following questions:

And yes, I actually need this kind of aliasing.

Upvotes: 55

Views: 63544

Answers (5)

Markus Lenger
Markus Lenger

Reputation: 527

You could use the following code to cast your data:

template<typename T, typename F>
struct alias_cast_t
{
    union
    {
        F raw;
        T data;
    };
};

template<typename T, typename F>
T alias_cast(F raw_data)
{
    alias_cast_t<T, F> ac;
    ac.raw = raw_data;
    return ac.data;
}

Example usage:

unsigned int data = alias_cast<unsigned int>(raw_ptr);

Upvotes: 13

moonshadow
moonshadow

Reputation: 89155

In order:

  • Yes. GCC will assume that the pointers cannot alias. For instance, if you assign through one then read from the other, GCC may, as an optimisation, reorder the read and write - I have seen this happen in production code, and it is not pleasant to debug.

  • Several. You could use a union to represent the memory you need to reinterpret. You could use a reinterpret_cast. You could cast via char * at the point where you reinterpret the memory - char * are defined as being able to alias anything. You could use a type which has __attribute__((__may_alias__)). You could turn off the aliasing assumptions globally using -fno-strict-aliasing.

  • __attribute__((__may_alias__)) on the types used is probably the closest you can get to disabling the assumption for a particular section of code.

For your particular example, note that the size of an enum is ill defined; GCC generally uses the smallest integer size that can be used to represent it, so reinterpreting a pointer to an enum as an integer could leave you with uninitialised data bytes in the resulting integer. Don't do that. Why not just cast to a suitably large integer type?

Upvotes: 62

icecrime
icecrime

Reputation: 76815

Have you looked into this answer ?

The strict aliasing rule makes this setup illegal, two unrelated types can't point to the same memory. Only char* has this privilege. Unfortunately you can still code this way, maybe get some warnings, but have it compile fine.

Upvotes: 5

Šimon T&#243;th
Šimon T&#243;th

Reputation: 36451

Strict aliasing is a compiler option, so you need to turn it off from the makefile.

And yes, it can generate incorrect code. The compiler will effectively assume that foobar and pi aren't bound together, and will assume that *pi won't change if foobar changed.

As already mentioned, use static_cast instead (and no pointers).

Upvotes: 3

CashCow
CashCow

Reputation: 31445

But why are you doing this? It will break if sizeof(foo) != sizeof(int). Just because an enum is like an integer does not mean it is stored as one.

So yes, it could generate "potentially" wrong code.

Upvotes: 11

Related Questions