Table of Contents
Bitwise flag manipulation is a powerful technique that allows you to use each bit in an 8-bit variable as an individual flag. This approach allows you to store 8 boolean values in a single byte, making it both memory-efficient and fast. In this tutorial, we will dive into bitwise flag manipulation, exploring how to set, read, and clear flags.
Flags are commonly used in programming to indicate the presence or absence of a certain condition. If you need to store multiple boolean values, one efficient way to do this is to use each bit in an 8-bit variable as an individual flag. This allows you to store 8 boolean values in just one byte, rather than using 8 separate boolean variables. This is not only memory-efficient but also can speed up the execution time as modern computers can manipulate bits in a byte very quickly.
Understanding Bits and Bytes
Before we start, let’s understand what bits and bytes are. A bit is the most basic unit of information in computing and digital communications. The name is a portmanteau of binary digit. The bit represents a logical state with one of two possible values. These values are most commonly represented as either “1” or “0”.
A byte is a unit of digital information that most commonly consists of 8 bits. Hence, an 8-bit variable can store values from 0 to 255 (2^8 – 1) in decimal or 00 to FF in hexadecimal.
The Essence of Bitwise Flag Manipulation
A lot of beginners usually use individual ‘int’ or ‘uint8_t’ for one flag. Some examples are:
int data_received_flag = 0; int data_transmitted_flag = 0; int threshold_crossed_flag = 0; OR uint8_t data_received_flag = 0; uint8_t data_transmitted_flag = 0; uint8_t threshold_crossed_flag = 0;
In this approach, the work gets done but there is a lot of wastage of memory. For some Microcontrollers, we deal with memory in the range of single digit KB. Wasting bytes is like a crime here.
But there is a better way to do it. The idea here is that each bit in an 8-bit variable can be treated as a separate flag – this gives us 8 flags in total. To do this, we use bitwise operations – specifically, the bitwise AND (&), OR (|), and NOT (~) operations.
Setting a Flag with Bitwise Flag Manipulation
Setting a flag is done with the bitwise OR operation. This ensures that a specific bit is set to 1, no matter what it was previously. For instance, to set the second bit in our 8-bit variable, we would do:
flags |= 0b00000010;
or, it’s often more readable to use hexadecimal or a bit shift operation:
flags |= 0x02; // hexadecimal flags |= (1 << 1); // bit shift
Checking a Flag with Bitwise Flag Manipulation
To check if a flag is set, we use the bitwise AND operation. This will return a non-zero result if the flag is set, and 0 if the flag is not set. For example, to check the second bit:
if (flags & 0x02) { // The second bit is set } else { // The second bit is not set }
Clearing a Flag with Bitwise Flag Manipulation
To clear a flag (set it to 0), we use the bitwise AND operation with the bitwise NOT operation. This ensures that a specific bit is set to 0, no matter what it was previously. For instance, to clear the second bit:
flags &= ~0x02;
or using a bit shift operation:
flags &= ~(1 << 1);
Flipping a Flag with Bitwise Flag Manipulation
To flip a flag (change from 0 to 1, or 1 to 0), we use the bitwise XOR operation. This will set a specific bit to its opposite, no matter what it was previously:
flags ^= 0x02;
or using a bit shift operation:
flags ^= (1 << 1);
Defining Function like Macros for Checking, setting and clearing flags:
Doing the operations multiple times in a program can get cumbersome and also prone to errors and bug. So, we can define some C macros that can be used to set, reset, and check flags in an 8-bit variable. These macros use the same bitwise operations we discussed earlier.
// Macro to set a flag #define FLAG_SET(var, flag) ((var) |= (1 << (flag))) // Macro to clear a flag #define FLAG_CLEAR(var, flag) ((var) &= ~(1 << (flag))) // Macro to check a flag #define FLAG_CHECK(var, flag) ((var) & (1 << (flag))) // Macro to toggle a flag #define FLAG_TOGGLE(var, flag) ((var) ^= (1 << (flag)))
Check out How to use #define Macros in C – NerdyElectronics
These macros can be used in the code as follow:
uint8_t flags = 0; // Our 8-bit variable that will store the flags // Set the 2nd flag FLAG_SET(flags, 1); // Check the 2nd flag if (FLAG_CHECK(flags, 1)) { printf("The 2nd flag is set.\n"); } else { printf("The 2nd flag is not set.\n"); } // Clear the 2nd flag FLAG_CLEAR(flags, 1); // Toggle the 2nd flag FLAG_TOGGLE(flags, 1);
Defining Macros for individual Flags:
We can make it more readable and easy to work with by defining MACROS for each individual flag:
#define data_received_flag 0 #define data_transmitted_flag 1 #define threshold_crossed_flag 2
By defining these flags as MACROS, what we are trying to do is as shown in the following diagram:
Now, our code can look like this:
#define data_received_flag 0 #define data_transmitted_flag 1 #define threshold_crossed_flag 2 // Macro to set a flag #define FLAG_SET(var, flag) ((var) |= (1 << (flag))) // Macro to clear a flag #define FLAG_CLEAR(var, flag) ((var) &= (1 << (flag))) // Macro to check a flag #define FLAG_CHECK(var, flag) ((var) & (1 << (flag))) // Macro to toggle a flag #define FLAG_TOGGLE(var, flag) ((var) ^= (1 << (flag))) int main() { uint8_t flags = 0; // Our 8-bit variable that will store the flags // Set the 2nd flag FLAG_SET(flags, data_received_flag); FLAG_SET(flags, data_transmitted_flag); // Check the 2nd flag if (FLAG_CHECK(flags, data_transmitted_flag )) { printf("The 2nd flag is set.\n"); } else { printf("The 2nd flag is not set.\n"); } // Clear the 2nd flag FLAG_CLEAR(flags, data_transmitted_flag ); // Toggle the 2nd flag FLAG_TOGGLE(flags, data_transmitted_flag ); return 0; }
Conclusion
This method is very space efficient, as it allows us to store 8 flags in a single byte. It’s also computationally efficient, as bitwise operations are very fast. However, it can be a bit harder to read than simply using separate variables. Use it a few times and it becomes very very easy.
Vivek is a Senior Embedded Innovation Specialist. He has been working on Embedded Systems for the past 10 years. He loves to share his knowledge and train those who are interested. Nerdyelectronics.com was started out of this interest.