arrayspointersexceptionmemory-alignmentmemory-access

Why is an "alignment exception" thrown when accessing an unaligned uint16 array with a pointer but not when accessing the array using subscripts?


I will use the following code to explain my question:

typedef struct __attribute__((packed))
{
    uint8_t  var;
    uint16_t array[3];
}struct_t;

uint8_t frame[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD};

volatile struct_t *ptr = NULL;
volatile uint16_t *u16ptr = NULL;
volatile uint16_t a;
volatile uint16_t b;

//******************************************************************************************
int main(int argc, char** argv)
{
    ptr = (struct_t*)frame;
    u16ptr = ptr->array;

    a = ptr->array[0]; // <---- NO Memory Access Exception here 
    b = *u16ptr;       // <---- Memory Access Exception here!
}

In that piece of code, I have forced array to be memory unaligned to have the memory access exception.

So my question is, why is an "alignment exception" thrown when accessing the unaligned uint16 array through a pointer but not when accessing the array using array subscripts?

None of the resources I've found so far explains why is this if both lines a = ptr->array[0]; and b = *u16ptr; are accessing the exact same unaligned memory.

Could someone please explain or point in the right direction?


Solution

  • Attempting to read a 16-bit value from an unaligned address will trap on some architecutres. Although the Standard defines the behavior of x[y] as equivalent to *((x)+(y)), which would cause an array to be decomposed into a pointer and then fetch a value from that address (trapping if the address isn't aligned), both clang and gcc treat structOrUnion.memberArray[index] as an lvalue that identifies part of a member of structOrUnion, as opposed to an expression that decomposes structOrUnion.memberArray to a pointer, forgets where the pointer came from, and then indexing that.

    If the [] operators are used directly on an unaligned array which a member of a "packed" struct, or on any aarray-type member of a union, clang and gcc will do what they need to do to access the storage in question. If, however, such an array is decomposed to a pointer, neither clang nor gcc will reliably allow it to be used to access an object of its type.