Akay
Akay

Reputation: 1132

Extracting bit positions in a union of 16 bits in C

I want to declare a union in C which mainly contains a 16-bit word, and I should be able to read/write the LSP 7 bits and 8th bit separately for each byte in the 16-bit word, so I declared something like:

typedef union {
    uint8_t full_8_char_byte[2];
    uint8_t ascii_7_bits_word_0 : 7; /* another way of representing LSP 7 ASCII bits */
    uint8_t parity_bit_word_0 : 1;   /* another way of representing MSP parity bit */
    uint8_t ascii_7_bits_word_1 : 7; /* another way of representing LSP 7 ASCII bits */
    uint8_t parity_bit_word_1 : 1;   /* another way of representing MSP parity bit */
} ip_char_t;

Now, when I write 7-bit values to these individual bytes in the 16-bit word:

x.full_8_char_byte[0] = 0x7E;
x.full_8_char_byte[1] = 0x7E;

The following simple program:

int main()
{
    ip_char_t x;
    x.full_8_char_byte[0] = 0x7E;
    x.full_8_char_byte[1] = 0x7E;

    printf("%c   %c\n", x.full_8_char_byte[1], x.full_8_char_byte[0]);
    printf("%u   %u\n", x.full_8_char_byte[1], x.full_8_char_byte[0]);
    printf("%X   %X\n\n", x.full_8_char_byte[1], x.full_8_char_byte[0]);

    printf("%c   %c\n", x.ascii_7_bits_word_1, x.ascii_7_bits_word_0);
    printf("%u   %u\n", x.ascii_7_bits_word_1, x.ascii_7_bits_word_0);
    printf("%X   %X\n\n", x.ascii_7_bits_word_1, x.ascii_7_bits_word_0);

    printf("%d   %d\n", x.parity_bit_word_1, x.parity_bit_word_0);
    return 0;
}

gives the correct output as follows:

~   ~  
126   126   
7E   7E

~   ~ 
126   126 
7E   7E

0   0

but when I initialized x.full_8_char_byte[1] to a 8-bit value:

x.full_8_char_byte[0] = 0x7E;
x.full_8_char_byte[1] = 0xFF;

the output I get is:

�   ~ 
255   126 
FF   7E

~   ~ 
126   126 
7E   7E

0   0

Similar thing happens when I initialize x.full_8_char_byte[0] to 0xFF, my question is why the value in x.ascii_7_bits_word_1 and x.parity_bit_word_1 did not reflected per change in x.full_8_char_byte[1]?

Upvotes: 2

Views: 590

Answers (1)

user2371524
user2371524

Reputation:

Looking at this:

typedef union {
    uint8_t full_8_char_byte[2];
    uint8_t ascii_7_bits_word_0 : 7; /* another way of representing LSP 7 ASCII bits */
    uint8_t parity_bit_word_0 : 1;   /* another way of representing MSP parity bit */
    uint8_t ascii_7_bits_word_1 : 7; /* another way of representing LSP 7 ASCII bits */
    uint8_t parity_bit_word_1 : 1;   /* another way of representing MSP parity bit */
} ip_char_t;

The comments suggest you expect the 4 bitfield members to map the bits of the first array member. That won't work, because all members of a union are alternative possible contents, which also means every member will be placed at the very beginning. What you probably meant to write instead is

typedef union {
    uint8_t full_8_char_byte[2];
    struct {
        uint8_t ascii_7_bits_word_0 : 7; /* another way of representing LSP 7 ASCII bits */
        uint8_t parity_bit_word_0 : 1;   /* another way of representing MSP parity bit */
        uint8_t ascii_7_bits_word_1 : 7; /* another way of representing LSP 7 ASCII bits */
        uint8_t parity_bit_word_1 : 1;   /* another way of representing MSP parity bit */
    };
} ip_char_t;

So this union can either contain your array or a struct with bitfields.


Note that this doesn't solve your problem in a portable way: There are no strict guarantees about how your bitfield will be arranged, see Leushenko's comment for details! For solving this problem portably, you can for example provide macros to access the individual bits, e.g.

typedef uint8_t ip_char_t[2];

#define ascii_7_bits_word(x,i) ((x)[i] & 0x7f)
#define parity_bit_word(x,i) ((x)[i] >> 7)

Or, if you also need to write to the bits or want to enforce type safety, write (inline) functions instead.

Upvotes: 5

Related Questions