C Language | Structure Declarations | Bit Fields
This article explains how to divide structure members into smaller bit-sized fields. Bit fields package several pieces of bit-level information into a single structure.
Dividing Data into Bits
Bit fields are useful when structure members must store several values divided into small groups of bits.
Consider MIDI (Musical Instrument Digital Interface), the electronic music standard. This protocol was created in the early 1980s, when 8-bit CPUs were common, and it is still used for electronic instruments. Processors were much slower than they are today, and memory was both expensive and limited. Efficiently transmitting information and processing it quickly in a small amount of memory sometimes mattered more than simple code and elegant structures. One solution was to divide a byte into bit-sized fields.
A bit field can compress two pieces of information into one byte by assigning four bits to each value. It divides the representation of an implementation-dependent numeric type into fields measured in bits. This makes it possible to create members that occupy four bits or six bits. A bit field does not have an address, however, so it cannot be accessed through a pointer.
To use bit fields, specify a colon (:) and the field width after each member name in a structure declaration.
struct Msg {
unsigned int type : 1;
unsigned int attr : 3;
unsigned int id : 4;
};
The Msg structure has a one-bit type member, a three-bit attr member, and a four-bit id member. Bit fields are generally declared with unsigned int to make their unsigned nature explicit.
The memory layout of a structure that contains bit fields is highly implementation-dependent. The C language does not define how bit fields that cross byte boundaries are represented in memory. An implementation may allocate fields from either the most significant bit or the least significant bit of a storage unit.
Code 1
#include <stdio.h>
struct MidiMsg {
unsigned int status : 4;
unsigned int channel : 4;
unsigned int second : 8;
unsigned int third : 8;
};
int main() {
struct MidiMsg msg = { 9 , 0 , 0x3C , 40 };
printf(
"status = %d\nchannel = %d\n"
"second = %d\nthird = %d\n" ,
msg.status , msg.channel , msg.second , msg.third
);
return 0;
}
Code 1 declares a MidiMsg structure with bit fields. This structure represents a message sent to MIDI hardware. You do not need to know MIDI to understand the example. Notice that the MIDI message divides the status and channel information into four-bit units. On a 32-bit computer, the structure might have the following layout.
Table 1 - Possible Layout of MidiMsg on a 32-bit Computer
| unsigned int: 32 bits |
|---|
| status: 4 bits | channel: 4 bits | second: 8 bits | third: 8 bits | unused: 8 bits |
As noted above, the exact layout of bit fields depends on the implementation. The execution result should still match the expected values. To confirm that the fields are divided at bit boundaries, assign a value greater than 0xF to the status member. Because status is four bits wide, the higher bits are discarded.