Motivation
When programming close to the hardware, you often need to manipulate bits and flags efficiently. Doing this with bitwise operators and macros can get messy:
#define FLAG1 0x01
#define FLAG2 0x02
unsigned int flags = FLAG1 | FLAG2;
if (flags & FLAG1) {
// do something for FLAG1
}
Bit fields provide a cleaner way to work with bits and flags in C structures.
Syntax
type [declarator] : width;
- The
type
for thedeclarator
must beunsigned int
,signed int
, orint
. - The
width
must be less than or equal to the size of the underlyingtype
.
Improved readability
With bit fields, the code reads more like accessing normal struct members:
struct {
unsigned int FLAG1 : 1;
unsigned int FLAG2 : 1;
} flags;
flags.FLAG1 = 1;
if (flags.FLAG1) {
// do something for FLAG1
}
And it is obviously less error-prone.
Memory optimization
Bit fields also enable packing data efficiently into the smallest space needed. For example, if you write:
struct {
unsigned int isKeyword;
unsigned int isExtern;
unsigned int isStatic;
} flags;
You will use at least 3 * sizeof(unsigned int)
or 12 bytes to represent three small flags, that should only need three bits.
So if you write:
struct {
unsigned int isKeyword : 1;
unsigned int isExtern : 1;
unsigned int isStatic : 1;
} flags;
This uses up the same space as one unsigned int
, which is 4 bytes.
Hardware registers
Besides, bit fields are commonly used to represent hardware registers. If a 32-bit system register has a meaning for each bit, you can define it like:
struct {
unsigned int power : 1;
unsigned int clock : 1;
//...
} systemRegisters;
This maps cleanly to the hardware while abstracting away the nitty-gritty of bit shifts.
Note
Bit fields can also be used to store values larger than one bit, although flags are more common.
struct {
unsigned short icon : 8;
unsigned short color : 4;
unsigned short underline : 1;
unsigned short blink : 1;
} screen[25][80]; // The size of each structure is 2 bytes.