In C, we can specify size (in bits) of structure and union members. The idea is to use memory efficiently when we know that the value of a field or group of fields will never exceed a limit or is withing a small range.
For example, consider the following declaration of date without the use of bit fields.
#include <stdio.h> // A simple representation of the date struct date { unsigned int d; unsigned int m; unsigned int y; }; int main() { printf("Size of date is %lu bytes\n", sizeof(struct date)); struct date dt = { 31, 12, 2014 }; printf("Date is %d/%d/%d", dt.d, dt.m, dt.y); } |
Size of date is 12 bytes Date is 31/12/2014
The above representation of ‘date’ takes 12 bytes on a compiler where an unsigned int takes 4 bytes. Since we know that the value of d is always from 1 to 31, the value of m is from 1 to 12, we can optimize the space using bit fields.
#include <stdio.h> // Space optimized representation of the date struct date { // d has value between 1 and 31, so 5 bits // are sufficient unsigned int d : 5; // m has value between 1 and 12, so 4 bits // are sufficient unsigned int m : 4; unsigned int y; }; int main() { printf("Size of date is %lu bytes\n", sizeof(struct date)); struct date dt = { 31, 12, 2014 }; printf("Date is %d/%d/%d", dt.d, dt.m, dt.y); return 0; } |
Size of date is 8 bytes Date is 31/12/2014
However if the same code is written using signed int and the value of the fields goes beyond the bits allocated to the variable and something interesting can happen. For example consider the same code but with signed integers:
#include <stdio.h> // Space optimized representation of the date struct date { // d has value between 1 and 31, so 5 bits // are sufficient int d : 5; // m has value between 1 and 12, so 4 bits // are sufficient int m : 4; int y; }; int main() { printf("Size of date is %lu bytes\n", sizeof(struct date)); struct date dt = { 31, 12, 2014 }; printf("Date is %d/%d/%d", dt.d, dt.m, dt.y); return 0; } |
Size of date is 8 bytes Date is -1/-4/2014
The output comes out to be negative. What happened behind is that the value 31 was stored in 5 bit signed integer which is equal to 11111. The MSB is a 1, so it’s a negative number and you need to calculate the 2’s complement of the binary number to get its actual value which is what is done internally. By calculating 2’s complement you will arrive at the value 00001 which is equivalent to decimal number 1 and since it was a negative number you get a -1. A similar thing happens to 12 in which case you get 4-bit representation as 1100 which on calculating 2’s complement you get the value of -4.
Following are some interesting facts about bit fields in C.
1) A special unnamed bit field of size 0 is used to force alignment on next boundary. For example consider the following program.
#include <stdio.h> // A structure without forced alignment struct test1 { unsigned int x : 5; unsigned int y : 8; }; // A structure with forced alignment struct test2 { unsigned int x : 5; unsigned int : 0; unsigned int y : 8; }; int main() { printf("Size of test1 is %lu bytes\n", sizeof(struct test1)); printf("Size of test2 is %lu bytes\n", sizeof(struct test2)); return 0; } |
Size of test1 is 4 bytes Size of test2 is 8 bytes
2) We cannot have pointers to bit field members as they may not start at a byte boundary.
#include <stdio.h> struct test { unsigned int x : 5; unsigned int y : 5; unsigned int z; }; int main() { struct test t; // Uncommenting the following line will make // the program compile and run printf("Address of t.x is %p", &t.x;); // The below line works fine as z is not a // bit field member printf("Address of t.z is %p", &t.z;); return 0; } |
Output:
prog.c: In function 'main':
prog.c:14:1: error: cannot take address of bit-field 'x'
printf("Address of t.x is %p", &t.x);
^
3) It is implementation defined to assign an out-of-range value to a bit field member.
#include <stdio.h> struct test { unsigned int x : 2; unsigned int y : 2; unsigned int z : 2; }; int main() { struct test t; t.x = 5; printf("%d", t.x); return 0; } |
Output:
Implementation-Dependent
4) In C++, we can have static members in a structure/class, but bit fields cannot be static.
// The below C++ program compiles and runs fine struct test1 { static unsigned int x; }; int main() {} |
// But below C++ program fails in the compilation // as bit fields cannot be static struct test1 { static unsigned int x : 5; }; int main() {} |
Output:
prog.cpp:5:29: error: static member 'x' cannot be a bit-field
static unsigned int x : 5;
^
5) Array of bit fields is not allowed. For example, the below program fails in the compilation.
struct test { unsigned int x[10] : 5; }; int main() { } |
Output:
prog.c:3:1: error: bit-field 'x' has invalid type unsigned int x[10]: 5; ^
Exercise:
Predict the output of following programs. Assume that unsigned int takes 4 bytes and long int takes 8 bytes.
1)
#include <stdio.h> struct test { unsigned int x; unsigned int y : 33; unsigned int z; }; int main() { printf("%lu", sizeof(struct test)); return 0; } |
2)
#include <stdio.h> struct test { unsigned int x; long int y : 33; unsigned int z; }; int main() { struct test t; unsigned int* ptr1 = &t.x; unsigned int* ptr2 = &t.z; printf("%d", ptr2 - ptr1); return 0; } |
3)
union test { unsigned int x : 3; unsigned int y : 3; int z; }; int main() { union test t; t.x = 5; t.y = 4; t.z = 1; printf("t.x = %d, t.y = %d, t.z = %d", t.x, t.y, t.z); return 0; } |
4) Use bit fields in C to figure out a way whether a machine is little-endian or big-endian.
Applications –
- If storage is limited, we can go for bit-field.
- When devices transmit status or information encoded into multiple bits for this type of situation bit-fiels is most effiecient.
- Encryption routines need to access the bits within a byte in that situation bit-field is quite usefull.
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.
Recommended Posts:
- Inserting m into n such that m starts at bit j and ends at bit i.
- How to compile 32-bit program on 64-bit gcc in C and C++
- Inserting M into N such that m starts at bit j and ends at bit i | Set-2
- Minimum bit flips such that every K consecutive bits contain at least one set bit
- Position of rightmost set bit
- Add two bit strings
- Find position of the only set bit
- Toggle all the bits of a number except k-th bit.
- Toggling k-th bit of a number
- Find Duplicates of array using bit array
- Bit Tricks for Competitive Programming
- Check if two numbers are bit rotations of each other or not
- Set the K-th bit of a given number
- Remove one bit from a binary number to get maximum value
- Set the rightmost unset bit
- Josephus Problem Using Bit Magic
- Get the position of rightmost unset bit
- 1 to n bit numbers with no consecutive 1s in binary representation.
- Print 'K'th least significant bit of a number
- Toggle all bits after most significant bit

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.
