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
- structure in C: you should know in-depth
- Designated Initializers in C, You should know.
- Structure Padding and Alignment.
- Top 10 Structure Padding Interview Questions
- 100 C interview Questions.
- Interview questions on bitwise operators in C
- C++ Interview Questions.
- 10 questions about dynamic memory allocation.
- File handling in C.
- Pointer in C.
- C format specifiers.