avivgood2
avivgood2

Reputation: 237

Converting types via unions doesn't work when using structs

I want to be able to "concat" bytes together, so that if I have bytes 00101 and 010 the result will be 00101010

For this task I have written the following code:

#include <stdio.h>
typedef struct
{
    unsigned char bits0to5 : 6;
    unsigned char bits5to11 : 6;
    unsigned char bits11to15 : 4;
}foo;
typedef union
{
    foo bytes_in_one_form;
    long bytes_in_other_form : 16;
}bar;
int main()
{
    bar example;
    /*just in case the problem is that bytes_in_other_form wasn't initialized */
    example.bytes_in_other_form = 0;
    /*001000 = 8, 000101 = 5, 1111 = 15*/
    example.bytes_in_one_form.bits0to5 = 8;
    example.bytes_in_one_form.bits5to11 = 5;
    example.bytes_in_one_form.bits11to15 = 15;
    /*sould be 0010000001011111 = 8287*/
    /*NOTE: maybe the printf is wrong since its only 2 bytes and from type long?*/
    printf("%d", example.bytes_in_other_form); 
    /*but number printed is 1288*/
    return 0;
}

What have I done wrong? Unions should have all their member share the same memory, and both the struct and the long take up exactly 2 bytes.

Note:

For solutions that use entirely different algorithm, please note I need to be able to have the zeros at the start (so for example 8 = 001000 and not 1000, and the solution should match any number of bytes at any distribution (although understanding what I did wrong in my current algorithm is better) I should also mention I use ANSI C.

Thanks!

Upvotes: 0

Views: 61

Answers (1)

Tom Karzes
Tom Karzes

Reputation: 24082

This answer applies to the original question, which had:

typedef struct
{
    unsigned char bits0to5 : 6;
    unsigned char bits5to11 : 6;
    unsigned char bits11to15 : 4;
}foo;

Here's what's happening in your specific example (note that the results may vary from one platform to another):

The bit fields are being packed into char variables. If the next bit field doesn't fit into the current char, it skips to the next one. Additionally, you have little-endian addressing, so the char values appear right-to-left in the aliased long bit field.

So the layout of the structure fields is:

+--------+--------+--------+
|....cccc|..bbbbbb|..aaaaaa|
+--------+--------+--------+

Where aaaaaa is the first field, bbbbbb is the second field, cccc is the third field, and the . values are padding.

When storing your values, you have:

+--------+--------+--------+
|....1111|..000101|..001000|
+--------+--------+--------+

With zeroes in the pad bits, this becomes:

+--------+--------+--------+
|00001111|00000101|00001000|
+--------+--------+--------+

The other value in the union is aliased to the low-order 16 bits, so the value it picks up is:

+--------+--------+
|00000101|00001000|
+--------+--------+

This is 0x0508, which in decimal is 1288 as you saw.

If the structure instead uses unsigned long for the bit field types, then we have:

typedef struct
{
    unsigned long bits0to5 : 6;
    unsigned long bits5to11 : 6;
    unsigned long bits11to15 : 4;
}foo;

In this case, the fields are packed into an unsigned long as follows:

    -----+--------+--------+
    .....|11110001|01001000|
    -----+--------+--------+

The low-order 16 bits are 0xf148, which is 61768 in decimal.

Upvotes: 3

Related Questions