C Language Character types cannot be accessed through non-character types.


Example

If an object is defined with static, thread, or automatic storage duration, and it has a character type, either: char, unsigned char, or signed char, it may not be accessed by a non-character type. In the below example a char array is reinterpreted as the type int, and the behavior is undefined on every dereference of the int pointer b.

int main( void )
{
    char a[100];
    int* b = ( int* )&a;
    *b = 1;      

    static char c[100];
    b = ( int* )&c;
    *b = 2;

    _Thread_local char d[100];
    b = ( int* )&d;
    *b = 3;
}

This is undefined because it violates the "effective type" rule, no data object that has an effective type may be accessed through another type that is not a character type. Since the other type here is int, this is not allowed.

Even if alignment and pointer sizes would be known to fit, this would not exempt from this rule, behavior would still be undefined.

This means in particular that there is no way in standard C to reserve a buffer object of character type that can be used through pointers with different types, as you would use a buffer that was received by malloc or similar function.

A correct way to achieve the same goal as in the above example would be to use a union.

typedef union bufType bufType;
union bufType {
   char c[sizeof(int[25])];
   int i[25];
};

int main( void )
{
    bufType a = { .c = { 0 } }; // reserve a buffer and initialize
    int* b = a.i;      // no cast necessary
    *b = 1;      

    static bufType a = { .c = { 0 } };
    int* b = a.i;
    *b = 2;

    _Thread_local bufType a = { .c = { 0 } };
    int* b = a.i;
    *b = 3;
}

Here, the union ensures that the compiler knows from the start that the buffer could be accessed through different views. This also has the advantage that now the buffer has a "view" a.i that already is of type int and no pointer conversion is needed.