bit field in c

Bit field in c, you should know

In C language structure and union support a very important feature that is the bit field. The bit field allows the packing of data in a structure or union and prevents the wastage of memory.

Note: The layout of the bit-fields is implementation-defined that is the reason a lot of people are avoiding the use of bit-filed.

Syntax of bit fields in C:

In C language declaration of the bit-field structure or union is similar to the declaration of the normal structure or union, the main difference is that bit-field member is declared with a specified number of bits preceded by the colon.

struct
{
 type-specifier declarator opt : constant-expression
};

In the above declaration, constant-expression specifies the width of the field in bits and must be a non-negative integer value. If the value is zero, the declaration has no declarator.

The type-specifier for the declarator must be _Bool, signed int, unsigned int, or some other implementation-defined type. It is implementation-defined whether atomic types are permitted.

Example,

Let’s take an example to understand the structure bit field.

struct packed_data
{
    unsigned int data1:1;
    unsigned int data2:1;
    unsigned int data3:1;
    unsigned int data4:1;
    unsigned int data5:1;
    unsigned int data6:3;
    unsigned int data7:6;
} sPackData;

 

In the above example, ‘structure packed_data’ contains 7 members. In which five-member (data1 to data5) has 1 bit and 6th and 7th member has the 3 and 6 bits.

Way to access the member of bit-field:

sPackData.data6 = 3;

 

For learning more, you can signup for the free trial of this popular c video course by Kenny Kerr.

 

Use of bit field in embedded C?

Suppose a microcontroller GPIO Port has 8 pins and each pin are connected to the led. In that scenario using the bitfield, we can easily change the status of the led. Let’s see a small example where I am trying to explain how to access GPIO Pin using the bit-field.

So first we need to create a bit-field structure for mapping with GPIO Port of given micro-controller.

typedef union
{

    struct
    {

        uint8_t LED1 : 1;
        uint8_t LED2 : 1;
        uint8_t LED3 : 1;
        uint8_t LED4 : 1;
        uint8_t LED5 : 1;
        uint8_t LED6 : 1;
        uint8_t LED7 : 1;
        uint8_t LED8 : 1;
    };

    uint8_t AllLedState;

} LED_BAR_STATE;

 

Create a pointer to the above-created bit-field ‘LED_BAR_STATE’ and assign the address of the GPIO Port.

volatile LED_BAR_STATE *pLedState = (volatile LED_BAR_STATE *)0xE002C000;

 

Now you can access the individual led using the pointer.

pLedState->LED1 = 1;

pLedState->LED2 = 0;

 

Note: Here, I am only describing, how is the bit-field work. I am not suggesting using bit-field in the mapping of a hardware register because the allocation of bit-field depends upon the compiler.

Might be the result of one compiler can be different from another compiler. So we should avoid the compiler-dependent code, In simple words, avoid using bit fields for the mapping of the hardware register.

 

Some important points about the bit field in c

  • If we are compiled the same C program that uses the bit-field on a different system, the result of the program may vary (The c program may not work properly).
  • The order of allocation of bit-fields within a unit low-order to high-order or high-order to low-order ( depend on endianness )is implementation-defined.
#include <stdio.h>

#define CHAR_BITS  8  // size of character

#define INT_BITS  ( sizeof(int) * CHAR_BITS) //bits in integer


// Use to print the data in binary format
void PrintInBinary(unsigned n)
{
    short int iPos;

    for (iPos = (INT_BITS -1) ; iPos >= 0 ; iPos--)
    {
        (n & (1 << iPos))? printf("1"): printf("0");
    }

}

struct sBitField
{
    unsigned int  Data00:8;
    unsigned int  Data01:8;
    unsigned int  Data02:8;
    unsigned int  Data03:8;

};



int main()
{
    struct sBitField sBits ;

    int *pData = (int*)&sBits;

    *pData = 0;     //Clear all bits

    PrintInBinary(*pData );  // Print bits
    putchar('\n');

    sBits.Data00 = 0x11;
    PrintInBinary(*pData);  // Print bits
    putchar('\n');

    sBits.Data01 = 0x22;
    PrintInBinary(*pData );  // Print bits
    putchar('\n');

    sBits.Data02 = 0x33;

    PrintInBinary(*pData);  // Print bits
    putchar('\n');

    sBits.Data03 = 0x44;
    PrintInBinary(*pData);  // Print bits
    putchar('\n');

    return 0;
}

Output:

When running on a machine (Linux):

00000000000000000000000000000000
00000000000000000000000000010001
00000000000000000010001000010001
00000000001100110010001000010001
01000100001100110010001000010001

  • If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined.
#include <stdio.h>

struct sData
{
    unsigned int a: 2;
    unsigned int b: 2;
    unsigned int c: 2;
};


int main()
{
    struct sData data;

    data.a = 5;

    printf("%d", data.a );

    return 0;
}

Output:

Implementation-Dependent

  • We can not create a pointer to the bit-field and also not use the address-of operator (&) to the bit-field member.
#include <stdio.h>

struct sData
{
    unsigned int a: 2;
    unsigned int b: 2;
    unsigned int c: 2;
};


int main()
{
    struct sData data;

    data.a = 2;

    printf("Address of data.a =  %p", &data.a );

    return 0;
}

Output:

[Error] cannot take address of bit-field ‘a’

  • We can not create an array of a bit field in c.
#include <stdio.h>

struct sData
{
    unsigned int a: 2;
    unsigned int b[5]: 2;
};


int main()
{
    struct sData data;

    data.a = 2;

    return 0;
}


Output:

[Error] bit-field ‘b’ has an invalid type.

  • The bit fields must also be long enough to contain the bit pattern. See the below example,
struct sData
{
    unsigned int a: 2;

    short b: 17; /* Illegal! */

    unsigned int c: 2;
};
  • The alignment of the addressable storage unit is unspecified.
  • If enough space remains, a bit field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit.
  • A bit field declaration with no declarator is called an unnamed bit-field. If the width of the unnamed bit-field is 0 (zero), it indicates that no further bit-field is to be packed into the unit in which the previous bitfield, if any, was placed.

See the below example, here I have created two structure. In the second structure, I am using the unnamed bit-field with 0 widths for the force unalignment.

#include <stdio.h>

// A structure without forced alignment
typedef struct
{
    unsigned int data1: 5;
    unsigned int data2: 8;
} sData1;


// A structure with forced alignment
typedef struct
{
    unsigned int data1: 5;
    unsigned int: 0;
    unsigned int data2: 8;
} sData2;


int main()
{
    printf("Size of sData1 = %d\n", sizeof(sData1));

    printf("Size of sData2 = %d\n", sizeof(sData2));

    return 0;
}

Output:

Size of sData1 = 4
Size of sData2 = 8

  • We can not calculate the size of the bit field in c using the sizeof operator.
#include <stdio.h>

struct sData
{
    unsigned int a: 2;
    unsigned int b: 2;
    unsigned int c: 2;
};


int main()
{
    struct sData data;


    printf("Sizeof of data.a =  %d", sizeof(data.a));

    return 0;
}

Output:

[Error] ‘sizeof’ applied to a bit field.

 

Recommended Post