16.10 – Bit fields in C language – Quiz

by subbu on September 27, 2014

Bit fields in C language

It is another user defined type in C language used to allocate the memory in bits. It is so useful where space is so critical.

First of all we will see why bit fields is needed. Say for example, when we want to store a date with the details of date, month and year then we need three short type variables that take nearly 6 Bytes because each short type member takes two bytes.

struct date
{
 short dt;
 short mn;
 short yr;
};

struct date a;

Here struct date type of variable “a” takes 6 bytes that is 48 bits, generally we don’t need this much memory. It is enough to have just 23 bits of memory that is 6 bits to store maximum date of 31, 5 bits to store maximum month of 12 and 12 bits to store maximum year of 2024.

Exact memory as per our requirement (bit wise) can be defined as

struct date
{
 short dt:6;
 short mn:5;
 short yr:12;
};

Here the member “dt” takes 6 bits and can hold from -32 to +31, “mn” takes 5 bits and can hold -16 to +15 and “yr” takes 12 bits and can hold from -2048 to +2047.

In the above struct every bit field is a signed type, hence the first bit is a sign specific bit. Overflow and underflow rules apply even to the bit fields. Actually, here we don’t need to store the sign in any field. Hence we can further save the memory by defining the bit fields as unsigned type.

#include<stdio.h>
struct date
{
 unsigned short dt:5;
 unsigned short mn:4;
 unsigned short yr:11;
};
int main()
{
 struct date a;
 a.dt=30;
 a.mn=12;
 a.yr=2015;
 printf("Date: %u/%u/%u",a.dt,a.mn,a.yr);
 printf("\nMemory Taken %d bytes",sizeof(a));
 return 0;
}

Output
Date: 30/12/2015
Memory Taken 3 bytes

In the above example, though the actual memory taken by the struct variable “a” is of 20 bits, the sizeof() operator showing that it is taking 3 bytes because the sizeof() of operator can’t show the bits but only bytes. Hence showing 3 Bytes instead of 20 bits.

We can’t print the address of a bit field

Generally bytes have address but, bits have no address hence the address of operator & could not be used to access the address of bit fields

Trying to access the address of bit fields result compilation error

#include<stdio.h>
struct date
{
 unsigned short dt:5;
 unsigned short mn:4;
 unsigned short yr:11;
};
int main()
{
 struct date a;
 printf("%u\n%u\n%u",&a.dt,&a.mn,&a.yr);
 return 0;
}

Output:
Error: Illegal to take the address of bit field

A bit field struct may have normal members

Along with bit fields, a bit field struct can have other members like primary, derived and user defined types as members but, the memory allocation of normal members would be allocated next to the bit field.

#include<stdio.h>
struct product
{
 unsigned short sno:5;
 short amt;
};
int main()
{
 struct product x;
 x.sno=12;
 x.amt=4500;
 printf("Sno: %u",x.sno);
 printf("\nAmount: %d",x.amt);
 printf("\nAddress of x: %u",&x);
 printf("\nAddress of x.amt: %u",&x.amt);
 return 0;
}

Output in turbo C:
Sno: 12
Amount: 4500
Address of x: 65522
Address of x.amt: 65523

Output in Ubuntu gcc
Sno: 12
Amount: 4500
Address of x: 3215385420
Address of x.amt: 3215385422

With the above example it is learned that, the struct variable is not optimized in Turbo C because the address of x.amt is not aligned according to its address but allocated just next to the bit field

In case of Ubuntu gcc the struct variable is optimized, because the address of x.amt is aligned according to its address.

We can instruct gcc to not go for struct padding by using _ _attribute_ _((packed)) as

struct product
{
 unsigned short sno:5;
 short amt;
}__attribute__((packed));
int main()
{
 struct product x;
 x.sno=12;
 x.amt=4500;
 printf("Sno: %u",x.sno);
 printf("\nAmount: %d",x.amt);
 printf("\nAddress of x: %u",&x);
 printf("\nAddress of x.amt: %u",&x.amt);
 return 0;
}

Output in Ubuntu gcc
Sno: 12
Amount: 4500
Address of x: 3215385420
Address of x.amt: 3215385421

Here the normal member next to the bit field is not aligned because of instruction to the compiler __attribute__((packed))

Empty bit field member

In gcc it is possible to optimize a normal member by introducing an empty member without any name and assigning with 0 just after the bit field and before the normal field as

#include<stdio.h>
struct product
{
 unsigned short sno:5;
 unsigned shor:0;       /* empty member */
 short amt;
}__attribute__((packed));
int main()
{
 struct product x;
 x.sno=12;
 x.amt=4500;
 printf("Sno: %u",x.sno);
 printf("\nAmount: %d",x.amt);
 printf("\nAddress of x: %u",&x);
 printf("\nAddress of x.amt: %u",&x.amt);
 return 0;
}

Output in Ubuntu gcc
Sno: 12
Amount: 4500
Address of x: 3215385420
Address of x.amt: 3215385422

In the above example x.amt is aligned though instructing the compiler to not optimize using __attribute__((packed)) because of the introduction of empty member after the bit field.

A word about the size of bit field

A bit field can’t be defined with the size more than its data size. Say for example a bit field can’t be defined as

unsigned short x:17;

because under any platform short type takes 2 Bytes that are 16 bits, specifying more than 16 is illegal and results compilation error.

Note: floating point types are not allowed as bit fields

Quiz:
1. What would be the output of following program?

#include <stdio.h>
struct num
{
 unsigned short x : 2;
 unsigned short y : 2;
};
int main()
{
 struct num a;
 a.x = 3;
 a.y = 1;
 printf("%d Bytes\n", sizeof(a));
 return 0;
}
Show Answer
Output
1 Bytes

Here two bit fields together take 4 bits that is within 1 byte, hence takes 1 byte only

2. What would be the output of  following program?

#include <stdio.h>
struct num
{
 unsigned int x:2;
 unsigned int y:2;
};
int main()
{
 struct num a;
 a.x = 3;
 a.y = 4;
 printf("x=%d\n", a.x);
 printf("y=%d\n", a.y);
 return 0;
}
Show Answer
Output
x=3
y=0

Here the bit fields x, y takes 2 bits, so the range of these bit fields are from 0 to 22 -1 that is 0 to 3.

The value of “x” would be 3 because the value assigned to “x” is within the range. The value of “y” would be 0 because assigning 4 would result overflow and falls towards 0.

3. What would be the output of  following program?

#include <stdio.h>
struct num
{
 int x : 7;
 int y : 2;
};
int main()
{
 struct num a;
 a.x = 65;
 a.y = 2;
 printf("x=%d\n",a.x);
 printf("y=%d\n",a.y);
 return 0;
}
Show Answer
Output
x=-63
y=-2

Here both the bit fields x, y are of signed types. The range of “x” would be -26 to +26-1 that is from -64 to 63. Assigning 65 would result overflow (-63)

The range of “y” would be -21 to +21-1 that is from -2 to 1. Assigning 2 would result overflow (-2)

4. What would be the output of  following program?

#include <stdio.h>
struct num
{
 unsigned int x : 1;
 unsigned int y : 1;
};
int main()
{
 struct num a;
 a.x = 1;
 a.y = 2;
 printf("x=%d\n", a.x);
 printf("y=%d\n",a.y);
 return 0;
}
Show Answer
Output
x=1
y=0

Here both the members “x” and “y” are unsigned, the range of which is from 0 to 21-1 that is from 0 to 1.

1 would be properly assigned to “x” but assigning 2 to “y” results overflow that falls value to 0

Previous post:

Next post: