YouTip LogoYouTip

C Bit Fields

C Bit-Fields

C language's bit-field is a special type of structure member that allows us to define members by specifying the number of bits they occupy.

If a program's structure contains multiple variables that act as switches, i.e., variables whose values are TRUE/FALSE, as shown below:

struct{
   unsigned int widthValidated;
   unsigned int heightValidated;
} status;

This structure requires 8 bytes of memory space, but in reality, we only store 0 or 1 in each variable. In this case, C language provides a better way to utilize memory space. If you use such variables inside a structure, you can define the variable's width to tell the compiler that you will only use those bits. For example, the above structure can be rewritten as:

struct{
   unsigned int widthValidated : 1;
   unsigned int heightValidated : 1;
} status;

Now, in the above structure, the status variable will occupy 4 bytes of memory space, but only 2 bits are used to store the values. If you use 32 variables, each with a width of 1 bit, the status structure will use 4 bytes. However, if you add just one more variable, using 33 variables, it will allocate the next segment of memory to store the 33rd variable, and at this point, it starts using 8 bytes. Let's look at the following example to understand this concept:

Example

#include<stdio.h>
#include<string.h>

struct{
   unsigned int widthValidated;
   unsigned int heightValidated;
}status1;

struct{
   unsigned int widthValidated : 1;
   unsigned int heightValidated : 1;
}status2;

int main(){
   printf("Memory size occupied by status1 : %dn", sizeof(status1));
   printf("Memory size occupied by status2 : %dn", sizeof(status2));
   return 0;
}

When the above code is compiled and executed, it produces the following result:

Memory size occupied by status1 : 8
Memory size occupied by status2 : 4

The features and usage of bit-fields are as follows:

  • When defining a bit-field, you can specify the width of the member, i.e., the number of bits the member occupies.
  • The width of a bit-field cannot exceed the size of its data type because the bit-field must fit within the integer type used.
  • The data type for a bit-field can be integer types such as int, unsigned int, signed int, or enumeration types.
  • A bit-field can be used alone or combined with other members to form a structure.
  • Accessing a bit-field is done using the dot operator (.), the same as accessing normal structure members.

Some information does not need to occupy an entire byte when stored; it only needs a few or even a single binary bit. For example, when storing a switch variable, which only has two states (0 and 1), it can be represented with 1 binary bit. To save storage space and simplify processing, C language provides another data structure called "bit-field" or "bit-field segment".

A so-called "bit-field" divides the binary bits within a byte into several different regions and specifies the number of bits for each region. Each region has a field name, allowing operations in the program by field name. In this way, several different objects can be represented using the binary bits of a single byte.

Typical examples:

  • When using 1 binary bit to store a switch variable, there are only two states: 0 and 1.
  • Reading external file formats – it can read non-standard file formats. For example: a 9-bit integer.

Defining Bit-Fields and Declaring Bit-Field Variables

Defining a bit-field is similar to defining a structure, with the following form:

struct Bit-field-structure-name {
    Bit-field list
};

Where the bit-field list has the following form:

type  : width ;

Below is a description of the variable elements in a bit-field:

Element Description
type Can only be one of three types: int (integer), unsigned int (unsigned integer), signed int (signed integer). It determines how the bit-field's value is interpreted.
member_name The name of the bit-field.
width The number of bits in the bit-field. The width must be less than or equal to the bit width of the specified type.

A variable with a predefined width is called a bit-field. A bit-field can store a number larger than 1 bit. For example, if you need a variable to store a value from 0 to 7, you can define a bit-field with a width of 3 bits, as shown below:

struct{
   unsigned int age : 3;
}Age;

The above structure definition instructs the C compiler that the age variable will only use 3 bits to store this value. If you try to use more than 3 bits, it cannot be done.

struct bs{
   int a:8;
   int b:2;
   int c:6;
}data;

The code above defines a structure named struct bs, where data is a structure variable of type bs, occupying a total of four bytes:

For bit-fields, their width cannot exceed the size of their data type. In this case, the size of the int type is usually 4 bytes (32 bits).

If adjacent bit-field members have the same type and the sum of their bit widths is less than the sizeof of that type, the subsequent field will be stored immediately after the previous one until it can no longer fit.

Let's look at another example:

struct packed_struct{
   unsigned int f1:1;
   unsigned int f2:1;
   unsigned int f3:1;
   unsigned int f4:1;
   unsigned int type:4;
   unsigned int my_int:9;
}pack;

The code above defines a structure named packed_struct, containing six member variables, where pack is a structure variable of type packed_struct.

Here, packed_struct contains 6 members: four 1-bit identifiers f1..f4, one 4-bit type, and one 9-bit my_int.

Let's look at the following example:

Example 1

#include <stdio.h>

struct packed_struct {
   unsigned int f1 :1;// 1-bit bit-field
   unsigned int f2 :1;// 1-bit bit-field
   unsigned int f3 :1;// 1-bit bit-field
   unsigned int f4 :1;// 1-bit bit-field
   unsigned int type :4;// 4-bit bit-field
   unsigned int my_int :9;// 9-bit bit-field
};

int main(){
   struct packed_struct pack;
   pack.f1=1;
   pack.f2=0;
   pack.f3=1;
   pack.f4=0;
   pack.type=7;
   pack.my_int=255;
   printf("f1: %un", pack.f1);
   printf("f2: %un", pack.f2);
   printf("f3: %un", pack.f3);
   printf("f4: %un", pack.f4);
   printf("type: %un", pack.type);
   printf("my_int: %un", pack.my_int);
   return 0;
}

The above example defines a structure named packed_struct that contains multiple bit-field members.

In the main function, a structure variable pack of type packed_struct is created, and values are assigned to each bit-field member.

Then, printf statements are used to print the value of each bit-field member.

The output is:

f1: 1
f2: 0
f3: 1
f4: 0
type: 7
my_int: 255

Example 2

#include<stdio.h>
#include<string.h>

struct{
   unsigned int age : 3;
}Age;

int main(){
   Age.age = 4;
   printf("Sizeof( Age ) : %dn", sizeof(Age));
   printf("Age.age : %dn", Age.age);
   Age.age = 7;
   printf("Age.age : %dn", Age.age);
   Age.age = 8;
   printf("Age.age : %dn", Age.age);
   return 0;
}

When the above code is compiled, it will produce a warning. When the above code is executed, it produces the following result:

Sizeof( Age ) : 4
Age.age : 4
Age.age : 7
Age.age : 0

Calculating byte size:

Example

#include <stdio.h>

struct example1 {
   int a :4;
   int b :5;
   int c :7;
};

int main(){
   struct example1 ex1;
   printf("Size of example1: %lu bytesn",sizeof(ex1));
   return 0;
}

In the above example, the example1 structure contains three bit-field members a, b, and c, occupying 4 bits, 5 bits, and 7 bits respectively.

The sizeof operator is used to calculate the byte size of the example1 structure, and the result is output:

Size of example1: 4 bytes

The following points should be noted about bit-field definitions:

  • A bit-field is stored in the same byte. If the remaining space in a byte is insufficient to store the next bit-field, the next bit-field will be stored starting from the next unit. You can also intentionally make a bit-field start from the next unit. For example:
    struct bs{
       unsigned a:4;
       unsigned :4;
       unsigned b:4;
       unsigned c:4;
    }
    In this bit-field definition, a occupies the first 4 bits of the first byte, the next 4 bits are filled with 0 to indicate they are unused, b starts from the second byte, occupying 4 bits, and c occupies 4 bits.
  • The width of a bit-field cannot exceed the length of the data type it is attached to. Member variables have types, and this type limits the maximum length of the member variable; the number after the colon cannot exceed this length.
  • A bit-field can be an unnamed bit-field, which is used only for padding or positioning. An unnamed bit-field cannot be used. For example:
    struct k{
       int a:1;
       int :2;
       int b:3;
       int c:2;
    };

From the above analysis, it can be seen that a bit-field is essentially a type of structure, but its members are allocated by binary bits.

Using Bit-Fields

The usage of bit-fields is the same as that of structure members, with the general form:

Bit-field variable name.Bit-field name
Bit-field variable name->Bit-field name

Bit-fields allow output in various formats.

See the following example:

Example

#include<stdio.h>

int main(){
   struct bs{
      unsigned a:1;
      unsigned b:3;
      unsigned c:4;
   }bit,*pbit;
   bit.a=1;
   bit.b=7;
   bit.c=15;
   printf("%d,%d,%dn",bit.a,bit.b,bit.c);
   pbit=&bit;
   pbit->a=0;
   pbit->b&=3;
   pbit->c|=1;
   printf("%d,%d,%dn",pbit->a,pbit->b,pbit->c);
}

In the above example program, a bit-field structure bs is defined, with three bit-fields a, b, and c. A variable bit of type bs and a pointer variable pbit pointing to type bs are declared. This indicates that bit-fields can also use pointers.

← C TypedefC Unions β†’