Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this UNION like a struct?

Tags:

c

struct

unions

I came across some code (it's in a library from Microchip) which has a union.

All was good, until I saw it assigned values to different members of the union right after each other. My immediate thought was "They are over writing the same location..." Then I decided to do a test. By every measure I thought I understood, this union should be one byte (8 bits). But it's not... it's 4 bytes.

#pragma pack(1)
typedef union _STATUS
{
    BYTE Val;
    struct {
        unsigned BC8 : 1;
        unsigned BC9 : 1;
        unsigned BSTALL : 1;
        unsigned DTSEN : 1; 
        unsigned INCDIS : 1;
        unsigned KEN : 1;   
        unsigned DTS : 1;   
        unsigned UOWN : 1;  
    };
    struct {
        unsigned BC8 : 1;
        unsigned BC9 : 1;
        unsigned PID0 : 1;
        unsigned PID1 : 1;
        unsigned PID2 : 1;
        unsigned PID3 : 1;
        unsigned : 1;
        unsigned UOWN : 1;
    };
    struct {
        unsigned : 2;
        unsigned PID : 4; 
        unsigned : 2;
    };
} STATUS;
void PrintIt() {
    printf("Size of UNION is %d \n", sizeof(STATUS));
}

It should be the largest of any member, which each member is only 8 bits.

The code that caught my eye and made me investigate this is:

STAT.BC9 = 0;
STAT.BC8 = 0;
STAT.Val |= byteToSend;

Which the third line merges into the values from the first and second.

So I wanted to test it, it's coming out as 4 bytes, not one. I even tested it in a few different compilers (hence the #pragma usage for MS Visual C).

Each member is exactly 8 bits, and the last two struct overlap to place the PID values in the same memory location. And yet this is 4 bytes every way I use a compiler to evaluate it.

Is there something in the behavior of adding structs to unions?

Any explanation is appreciated.

like image 833
SpacemanScott Avatar asked Sep 13 '21 13:09

SpacemanScott


2 Answers

While not explicitly specified in the C standard, bitfield will typically occupy a unit corresponding to the base type they are declared with.

In this case all of the bitfields are declared as unsigned. This type is probably 4 bytes on your system so the bitfields occupy a unit of that type.

If you change the types of the fields to unsigned char or uint8_t they should take up only one byte. Note that this assumes your compiler allows using these types for bitfields, although most do.

like image 150
dbush Avatar answered Oct 08 '22 15:10

dbush


C has the concept of implicit type. So unsigned will declare an unsigned int. But it gets weirder. The same goes if you only use const or static/auto.

const x = 5; // Declares a const int variable
static x;    // Declares a static int variable
const static unsigned x = 5; // Declares a const static unsigned int variable

What you want is an unsigned char.

It should be the largest of any member, which each member is only 8 bits.

It should be at least that size. Nothing prevents it from being bigger.

like image 23
klutt Avatar answered Oct 08 '22 15:10

klutt