Search code examples
cpointersstructbit-fields

Accessing individual bits of unsigned integer through pointer to struct with bit fields in C


Let's say we have a struct defined like:

struct S_BF {
    unsigned first_bit : 1;
    unsigned second_bit : 1;
    unsigned rest : 30;
}

And a pointer to S_BF struct:

struct S_BF*  bf;

If we have a variable:

unsigned int p = 0;

How can we make bf point to p so we can modify the value of p by modifying its bit fields, for example, if we want to change the value of p to 2, we can do this:

bf->second_bit = 1;

Solution

  • The problem with wanting to access the bits of unsigned p with a pointer of type struct S_BF* is that it violates the strict alias rule C11 Standard - §6.5 Expressions (p6,7). The rule is designed to prevent accessing an object of one type by using a pointer to another. (there are various exceptions, but the intent of the rule is to prevent the type-punning of pointers)

    The only standard conforming way to access the bits of an unsigned through a pointer to struct S_BF it to make the bits the same bits through a union between struct S_BF and unsigned. You must can access the bits of struct S_BF through a pointer to struct S_BF, and make the struct S_BF and unsigned the same through the union without violating the string aliasing rule. A short example would be:

    #include <stdio.h>
    
    struct S_BF {               /* bitfield struct */
        unsigned first_bit : 1;
        unsigned second_bit : 1;
        unsigned rest : 30;
    };
    
    union s2u {                 /* union bitfield struct/unsigned */
        struct S_BF s;
        unsigned u;
    };
    
    int main (void) {
    
        union s2u s2u = { .u = 0 };             /* union initialized zero */
        struct S_BF *bf = &s2u.s;               /* pointer to bitfield */
        unsigned sz = 32;                       /* no. of bits in unsigned */
    
        bf->second_bit = 1;         /* set second bit 1 */
        bf->rest = 0x2aaaaaaa;      /* alternating bits 1010101... */
    
        while (sz--)    /* loop sz times, using unsigned value in union */
            putchar (s2u.u >> sz & 1 ? '1' : '0');  /* output bits */
    
        putchar ('\n');
    }
    

    Example Use/Output

    The above example would result in the following output:

    $ ./bin/bitfield_union
    10101010101010101010101010101010