views:

5791

answers:

7

When asking about common undefined behavior in C, souls more enlightened than I referred to the strict aliasing rule.
What are they talking about?

+2  A: 

Strict aliasing is not allowing different pointer types to the same data.

This article should help you understand the issue in full detail.

Jason Dagit
A: 

In particular: avoiding the void pointer.

Magsol
+2  A: 

Type punning is a major example of breaking strict aliasing.

Chris Jester-Young
+17  A: 

The best explanation I have found is by Mike Acton, Understanding Strict Aliasing. It's focused a little on PS3 development, but that's basically just GCC.

From the article: "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.)"

So basically if you have an int* and a float* they are not allowed to point to the same memory location. If your code does not respect this, then the compiler's optimizer will most likely break your code.

The exception to the rule is a char*, which is allowed to point to any type.

Niall
Article has moved to http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
jwhitlock
+1  A: 

See Understanding Strict Aliasing.

Harold Ekstrom
+30  A: 

A situation that you may encounter aliasing would be if, say, you had some API that took a "raw" pointer to data that was of type unsigned int* or unsigned short*. This actually occurs internally where I work where unfortunately instead of passing char* around, we pass the 32-bit unsigned ints around.

So if I want to send a message to something:

struct Msg
{
   unsigned int a;
   unsigned int b;
};

int main()
{
   // Pass data to something; if the implementer of this API were
   // strict-aliasing-aware, he would have taken a char*, not a unsigned int*
   Msg* msg = new Msg();
   msg->a = 1;
   msg->b = 2;

   // stupidBuffer is an alias for msg.
   // yes I know there are endianess problems here, but my API is stupid and 
   // only works for one platform
   unsigned int* stupidBuffer = reinterpret_cast<unsigned int*>(msg);

   SendToStupidApi( stupidBuffer );   
}

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.

What you actually end up seeing is that because of the "strict aliasing rule" the compiler assumes that there is no relationship between stupidBuffer and msg therefore msg may be seen as not used by the program after creation (an unused variable) and thus will get completely optimized out. So the message we are sending via stupidBuffer to our stupid API just appears as uninitialized data to the receiver. This leads to lots of head scratching and cursing.

This gets reported as a compiler bug a lot, but its really due to strict aliasing in C99. If you're stuck with an API that doesn't care to take char*, and instead takes something else to communicate "raw data", and there's little hope of changing this, you may either need to implement a union:

union
{
   Msg msg;
   // Know what you're doing with your system and endianess/word-order
   unsigned int asBuffer[sizeof(T)];
};

or disable strict aliasing in your compiler (fno-strict-aliasing in gcc -- not sure about other compilers)

If a union is needed, here's a templated C++ solution for the above problem you could also consider.

// Know what you're doing with your system and endianess/word-order
// You could also parameterize the "unsigned int" type to make 
// different kinds of arrays (such as short or 64 bit).
template <class T>
union SharedMemory
{
    T instance;
    unsigned int array[sizeof(T) / sizeof(unsigned int)];
};

in use:

int main()
{
    // Know what your doing with your system and endianess/word-order
    SharedMemory<Msg> msg;
    msg.instance.a = 1;
    msg.instance.b = 2;

    SendToStupidApi(msg.array);
}

Just make sure when you alias you learn about endianess

Doug T.
A: 

I know this isn't a bug but it really ought to be. If the compiler is smart enough to issue a warning it should simply disable strict aliasing on the function that generates the warning.

Joshua