A simple bit-field can be used to describe things that may have a specific number of bits involved.
struct encoderPosition {
unsigned int encoderCounts : 23;
unsigned int encoderTurns : 4;
unsigned int _reserved : 5;
};
In this example we consider an encoder with 23 bits of single precision and 4 bits to describe multi-turn. Bit-fields are often used when interfacing with hardware that outputs data associated with specific number of bits. Another example could be communication with an FPGA, where the FPGA writes data into your memory in 32 bit sections allowing for hardware reads:
struct FPGAInfo {
union {
struct bits {
unsigned int bulb1On : 1;
unsigned int bulb2On : 1;
unsigned int bulb1Off : 1;
unsigned int bulb2Off : 1;
unsigned int jetOn : 1;
};
unsigned int data;
};
};
For this example we have shown a commonly used construct to be able to access the data in its individual bits, or to write the data packet as a whole (emulating what the FPGA might do). We could then access the bits like this:
FPGAInfo fInfo;
fInfo.data = 0xFF34F;
if (fInfo.bits.bulb1On) {
printf("Bulb 1 is on\n");
}
This is valid, but as per the C99 standard 6.7.2.1, item 10:
The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined.
You need to be aware of endianness when defining bit-fields in this way. As such it may be necessary to use a preprocessor directive to check for the endianness of the machine. An example of this follows:
typedef union {
struct bits {
#if defined(WIN32) || defined(LITTLE_ENDIAN)
uint8_t commFailure :1;
uint8_t hardwareFailure :1;
uint8_t _reserved :6;
#else
uint8_t _reserved :6;
uint8_t hardwareFailure :1;
uint8_t commFailure :1;
#endif
};
uint8_t data;
} hardwareStatus;