Search code examples
cstructsizeofunions

Union of structs with only bit fields, sizeof function doubling bytes, C


For some reason that i cant quite figure out my union of just structs containing bit fields is setting up twice as many bytes as is are necessary for any single struct.

#include <stdio.h>
#include <stdlib.h>

union instructionSet {
    struct Brane{
        unsigned int opcode: 4;
        unsigned int address: 12;
    } brane;
    struct Cmp{
        unsigned int opcode: 4;
        unsigned int blank: 1;
        unsigned int rsvd: 3;
        unsigned char letter: 8;
    } cmp;
    struct {
        unsigned int rsvd: 16;
    } reserved;
};

int main() {

    union instructionSet IR;// = (union instructionSet*)calloc(1, 2);

    printf("size of union %ld\n", sizeof(union instructionSet));
    printf("size of reserved %ld\n", sizeof(IR.reserved));
    printf("size of brane %ld\n", sizeof(IR.brane));
    printf("size of brane %ld\n", sizeof(IR.cmp));


    return 0;
}

All of the calls to sizeof return 4 however to my knowledge they should be returning 2.


Solution

  • There are a couple of problems here, first of all, your bitfield Brane is using unsigned int which is 4 byte.

    Even if you just use half of the bits, you still use a full 32-bit width unsigned int.

    Second, your Cmp bitfields uses two different field types, so you use 8-bit of the 32-bit unsigned int for your first 3 fields, and then you use a unsigned char for it's full 8-bit. Because of data alignement rules, this structure would be at least 6 bytes, but potentially more.

    If you wanted to optimize the size of your union to only take 16-bit. Your first need to use unsigned short and then you need to always use the same field type to keep everything in the same space.

    Something like this would fully optimize your union:

    union instructionSet {
        struct Brane{
            unsigned short opcode: 4;
            unsigned short address: 12;
        } brane;
        struct Cmp{
            unsigned short opcode: 4;
            unsigned short blank: 1;
            unsigned short rsvd: 3;
            unsigned short letter: 8;
        } cmp;
        struct {
            unsigned short rsvd: 16;
        } reserved;
    };
    

    This would give you a size of 2 all around.