This isn't just an issue with GCC but embedded compilers as well. On embedded systems its common to work with packed data structures to maximize serial protocol efficiency.
Lets say you have a packed structure with a type of
struct MyPackedStruct {
uint8_t byteValue;
uint32_t intValues[5];
}
Normally a compiler would add 3 extra bytes between the uint8_t value and the uint32_t array to keep alignment of the int array, but that won't be the case if you force it to be packed. This results in uint32 values that span two word boundaries. If you access one of those array values in code directly, the compiler is smart enough to perform 2 separate memory reads and combine the resulting value so you don't have to really think about it.But if you do something like this:
uint32_t *intValues = myPacktedStruct.intValues;
The compiler allows this, but the resulting intValues pointer loses all packed awareness, and trying to dereference that pointer will result in an unaligned memory access exception.Moral of the story - only use packed data types when serializing/deserializing a protocol stream. Avoid using packed data types 'at rest', because it can cause subtle issues like this. The downside is that it results in extra parsing work when converting between packed and unpacked types.