Pointer aliasing is a common pitfall:

warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

From gcc man page:

-fstrict-aliasing

Allow the compiler to assume the strictest aliasing rules applicable to the language being compiled. For C (and C++), this activates optimizations based on the type of expressions. In particular, an object of one type is assumed never to reside at the same address as an object of a different type, unless the types are almost the same. For example, an “unsigned int” can alias an “int”, but not a “void*” or a “double”. A character type may alias any other type.

Pay special attention to code like this:

  union a_union {
int i;
double d;
};

int f() {
union a_union t;
t.d = 3.0;
return t.i;
}


The practice of reading from a different union member than the one most recently written to (called “type-punning”) is common. Even with -fstrict-aliasing, type-punning is allowed, provided the memory is accessed through the union type. So, the code above works as expected. However, this code might not:

  int f() {
union a_union t;
int* ip;
t.d = 3.0;
ip = &t.i;
return *ip;
}


Similarly, access by taking the address, casting the resulting pointer and dereferencing the result has undefined behavior, even if the cast uses a union type, e.g.:

  int f() {
double d = 3.0;
return ((union a_union *) &d)->i;
}


The -fstrict-aliasing option is enabled at levels -O2, -O3, -Os.