Search code examples
cpointersstrict-aliasing

Can a pointer to struct alias an unsigned char array?


#include "stdio.h"  


/* array to store data receeived from CAN Bus */
unsigned char a[8] = {0xCD, 0xEF, 0x12, 0x34, 0x50, 0x00, 0x00, 0x00};

typedef struct {
    unsigned int a:12;
    unsigned int b:12;
    unsigned int c:12;
    unsigned int unused:28;
}test;

test *tptr;

int main(void)
{

    tptr = (test*)( (void*)&a); // is this line braking any aliasing rule

    if(tptr->a == 0xCDE)
    {
        printf("\n data received ok");

    }

    return 0;
}

I recently learned about problems due to pointer aliasing in C. I want to know if the above code is breaking any rules. Could it lead to problems?

I know that the bitfield's order is machine and implementation dependent. My question, however, is regarding pointer aliasing rules, which I want to understand more clearly


Solution

  • It does break strict aliasing. Some (saner) compilers do deduce that obviously tptr should be allowed to alias a, but this is not guaranteed by the standard. In fact, GCC will not consider tptr to be aliasing a.

    Another problem is the alignment of a. The x86 is quite tolerant in that regard, but many other architectures, including the ARM, are not.

    Consider (int)&a == 0xFFF02 and sizeof(int) == 4 on architecture that support fetching ints only from addresses that are themselves multiples of sizeof(int). Such an unaligned access would result in a bus error, if you do *(int*)&a or even worse the program accessing the wrong location.

    To stay clear of undefined behavior, remember that it is guaranteed that accesses through unsigned char* are possible without alignment or aliasing constraints, not the other way round. Thus use:

    test buf;
    can_recv((unsigned char*)&buf, sizeof buf);
    if(tptr->a == 0xCDE)
    {
        printf("\n data received ok");
    
    }
    

    Another portability problem, is that you expect that bitfield to be packed a certain way. The standard doesn't guarantee that however. If you want portable behavior, restrict yourself to the bitfield interface (bitfield.b1 = 1) and don't modify the bitfied by other means.

    If portability isn't a concern, make sure to set the appropriate compiler flags that guarantee the bitfield packing you're expecting.