Bits, Bools, and Bit-fields

Last modified by Microchip on 2024/01/22 23:01

Programs often need objects that can act as a flag to indicate a 1 or 0 value or a true or false state. There are several objects suitable for this task when you are using the MPLAB® XC Compilers, and these objects are explained in the following sections and summarised in the table at the end of this article.

Boolean Objects

The _Bool type is a standard C type available when using the C99 or later C standard. Use the <stdbool.h> header file when using this type, which allows you to use macros like true and false for the values held by these objects.

_Bool objects can be made any size by the compiler (provided they are able to store 0 and 1) however, many implementations allocate an entire byte for such objects, as is the case with all MPLAB XC compilers. You can determine the size of a _Bool object or type by using the sizeof() operator.

When any scalar value is converted to type _Bool, the result is 0 (false) if the value compares equal to 0; otherwise, the result is 1 (true). This conversion is markedly different from the usual integer conversion rules employed by the C language.

#include <stdbool.h>

_Bool buttonDown;
unsigned int state = 0x42;

buttonDown = state;     // buttonDown assigned 'true' (1)
if(buttonDown == true)
  processRequest();

As with ordinary objects, you can take the address of _Bool objects and define pointers to such objects. Structure members can be defined as type _Bool, if required; however, you might consider using bit-fields in this situation, discussed in the next section.

_Bool * bp;
struct {
 _Bool button1;
 _Bool button2;
} buttonState;

bp = &buttonDown;
buttonState.button1 = *bp;

Back to Top

Bit-fields

Bit-fields are special objects that can be used only as members inside structures. They are available in all implementations of standard C (including C90), although some aspects of their operation are implementation-defined.

The number of bits allocated to a bit-field is specified when it is defined. To create a flag, allocate just a single bit to a bit-field by following the bit-fields name with :1. Bit-fields are packed into bytes, and as their placement in those bytes is strictly specified by the order in which they are defined in the structure, structures containing bit-fields are commonly used with unions to allow access to an entire byte (or bytes) and the bits which make up that byte (or bytes).

union {
 unsigned char byte;
 // a structure with 8 single bit bit-field objects, overlapping the union member "byte"
 struct {
   unsigned b0:1;
   unsigned b1:1;
   unsigned b2:1;
   unsigned b3:1;
   unsigned b4:1;
   unsigned b5:1;
   unsigned b6:1;
   unsigned b7:1;
  };
} byte_u;

if(byte_u.byte = 0x10)  // access the entire byte
   byte_u.b4 = 1;      // access just one bit in that byte

Bit-fields are recognized as having an integer type. Conversions from scaler values wider than the bit-field are usually performed by truncating the higher-order bits in the value that are not represented in the bit-field destination. You cannot take the address of bit-field objects, nor define pointers to such objects, but you can perform these actions against the entire structure in which the bit-fields reside.

Back to Top

Bit Objects

The __bit type is a non-standard type implemented only by MPLAB XC8 when building for PIC devices, and hence projects using this type may not be portable to other projects or compilers. There are some restrictions on when objects of type __bit can be used, for example, they cannot be auto objects, but they can be qualified static, allowing them to be defined locally within a function.

__bit powerOn;

int func(void) {
   static __bit flameOn;
   // ...
}

These objects are always one bit in size, and you cannot specify a __bit object or type as the argument to the sizeof() operator. The XC8 compiler will pack eight of these objects into one byte and access them using bit-orientated instructions, where possible.

The __bit type is an integer type (the signedness of single-bit objects does not make sense) so conversions from wider scaler values to __bit are performed by truncating all but the least significant bit in the value, potentially producing a different result to that obtained when assigning the same value to a _Bool.

__bit buttonDown;
unsigned int state = 0x42;

buttonDown = state;     // buttonDown assigned 0 (false)
if(buttonDown == 1)
  processRequest();

__bit objects are always represented by bit addresses (as opposed to conventional byte addresses), and bit addresses are shown for __bit objects and sections in list and map files. It is for this reason that you cannot take the address of __bit objects, nor can you define pointers to such objects. Structure members cannot be an object of type __bit.

Back to Top

Summary

The information in the above sections has been summarised in this table to help you select the best C type for your application. Check your favorite C language text or your compiler's user's guide for more information.

TypePortabilitySizeValueIndirect
access?
sizeof(type)?Structure
member?
_BoolC99 or lateroften 1 bytebooleanyesyesyes
bit-fieldStandard C1 or more bits,
as specified
integernonoalways
__bitXC8 only1 bitintegernonono

Back to Top