#include <bitset>
#include <iostream>
int main()
// Define a bunch of physical/emotional states
[[maybe_unused]] constexpr std::bitset<8> isHungry { 0b0000'0001 };
[[maybe_unused]] constexpr std::bitset<8> isSad { 0b0000'0010 };
[[maybe_unused]] constexpr std::bitset<8> isMad { 0b0000'0100 };
[[maybe_unused]] constexpr std::bitset<8> isHappy { 0b0000'1000 };
[[maybe_unused]] constexpr std::bitset<8> isLaughing { 0b0001'0000 };
[[maybe_unused]] constexpr std::bitset<8> isAsleep { 0b0010'0000 };
[[maybe_unused]] constexpr std::bitset<8> isDead { 0b0100'0000 };
[[maybe_unused]] constexpr std::bitset<8> isCrying { 0b1000'0000 };
std::bitset<8> me{}; // all flags/options turned off to start
me |= (isHappy | isLaughing); // I am happy and laughing
me &= ~isLaughing; // I am no longer laughing
// Query a few states (we use the any() function to see if any bits remain set)
std::cout << std::boolalpha; // print true or false instead of 1 or 0
std::cout << "I am happy? " << (me & isHappy).any() << '\n';
std::cout << "I am laughing? " << (me & isLaughing).any() << '\n';
return 0;
Two notes here: First, std::bitset doesn’t have a nice function that allows you to query bits using a bit mask. So if you want to use bit masks rather than positional indexes, you’ll have to use Bitwise AND to query bits. Second, we make use of the any() function, which returns true if any bits are set, and false otherwise to see if the bit we queried remains on or off.
When are bit flags most useful?
Astute readers may note that the above examples don’t actually save any memory. 8 separate booleans values would normally take 8 bytes. But the examples above (using std::uint8_t) use 9 bytes -- 8 bytes to define the bit masks, and 1 byte for the flag variable!
Bit flags make the most sense when you have many identical flag variables. For example, in the example above, imagine that instead of having one person (me), you had 100. If you used 8 Booleans per person (one for each possible state), you’d use 800 bytes of memory. With bit flags, you’d use 8 bytes for the bit masks, and 100 bytes for the bit flag variables, for a total of 108 bytes of memory -- approximately 8 times less memory.
For most programs, the amount of memory saved using bit flags is not worth the added complexity. But in programs where there are tens of thousands or even millions of similar objects, using bit flags can reduce memory use substantially. It’s a useful optimization to have in your toolkit if you need it.
There’s another case where bit flags and bit masks can make sense. Imagine you had a function that could take any combination of 32 different options. One way to write that function would be to use 32 individual Boolean parameters:
void someFunction(bool option1, bool option2, bool option3, bool option4, bool option5, bool option6, bool option7, bool option8, bool option9, bool option10, bool option11, bool option12, bool option13, bool option14, bool option15, bool option16, bool option17, bool option18, bool option19, bool option20, bool option21, bool option22, bool option23, bool option24, bool option25, bool option26, bool option27, bool option28, bool option29, bool option30, bool option31, bool option32);
Hopefully you’d give your parameters more descriptive names, but the point here is to show you how obnoxiously long the parameter list is.
Then when you wanted to call the function with options 10 and 32 set to true, you’d have to do so like this:
someFunction(false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true);
This is ridiculously difficult to read (is that option 9, 10, or 11 that’s set to true?), and also means you have to remember which argument corresponds to which option (is setting the “edit flag” the 9th, 10th, or 11th parameter?).
Instead, if you defined the function using bit flags like this:
void someFunction(std::bitset<32> options);
Then you could use bit flags to pass in only the options you wanted:
someFunction(option10 | option32);
This is much more readable.
This is one of the reasons OpenGL, a well regarded 3d graphic library, opted to use bit flag parameters instead of many consecutive Boolean parameters.
Here’s a sample function call from OpenGL:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color and the depth buffer
GL_COLOR_BUFFER_BIT and GL_DEPTH_BUFFER_BIT are bit masks defined as follows (in gl2.h):
#define GL_DEPTH_BUFFER_BIT 0x00000100
#define GL_STENCIL_BUFFER_BIT 0x00000400
#define GL_COLOR_BUFFER_BIT 0x00004000
Bit masks involving multiple bits
Although bit masks often are used to select a single bit, they can also be used to select multiple bits. Lets take a look at a slightly more complicated example where we do this.
Color display devices such as TVs and monitors are composed of millions of pixels, each of which can display a dot of color. Each dot of color is the result of combining three beams of light: one red, one green, and one blue (RGB). The intensity of these lights are varied to produce different colors.
Typically, the intensity of R, G, and B for a given pixel is represented by an 8-bit unsigned integer. For example, a red pixel would have R=255, G=0, B=0. A purple pixel would have R=255, G=0, B=255. A medium-grey pixel would have R=127, G=127, B=127.
When assigning color values to a pixel, in addition to R, G, and B, a 4th value called A is often used. “A” stands for “alpha”, and it controls how transparent the color is. If A=0, the color is fully transparent. If A=255, the color is opaque.
R, G, B, and A are normally stored as a single 32-bit integer, with 8 bits used for each component:
32-bit RGBA value
bits 31-24
bits 23-16
bits 15-8
bits 7-0
RRRRRRRR
GGGGGGGG
BBBBBBBB
AAAAAAAA
green
alpha
The following program asks the user to enter a 32-bit hexadecimal value, and then extracts the 8-bit color values for R, G, B, and A.
#include <cstdint>
#include <iostream>
int main()
constexpr std::uint32_t redBits{ 0xFF000000 };
constexpr std::uint32_t greenBits{ 0x00FF0000 };
constexpr std::uint32_t blueBits{ 0x0000FF00 };
constexpr std::uint32_t alphaBits{ 0x000000FF };
std::cout << "Enter a 32-bit RGBA color value in hexadecimal (e.g. FF7F3300): ";
std::uint32_t pixel{};
std::cin >> std::hex >> pixel; // std::hex allows us to read in a hex value
// use Bitwise AND to isolate the pixels for our given color,
// then right shift the value into the lower 8 bits
const std::uint8_t red{ static_cast<std::uint8_t>((pixel & redBits) >> 24) };
const std::uint8_t green{ static_cast<std::uint8_t>((pixel & greenBits) >> 16) };
const std::uint8_t blue{ static_cast<std::uint8_t>((pixel & blueBits) >> 8) };
const std::uint8_t alpha{ static_cast<std::uint8_t>(pixel & alphaBits) };
std::cout << "Your color contains:\n";
std::cout << std::hex; // print the following values in hex
// reminder: std::uint8_t will likely print as a char
// we static_cast to int to ensure it prints as an integer
std::cout << static_cast<int>(red) << " red\n";
std::cout << static_cast<int>(green) << " green\n";
std::cout << static_cast<int>(blue) << " blue\n";
std::cout << static_cast<int>(alpha) << " alpha\n";
return 0;
This produces the output:
Enter a 32-bit RGBA color value in hexadecimal (e.g. FF7F3300): FF7F3300
Your color contains:
ff red
7f green
33 blue
0 alpha
In the above program, we use a bitwise AND to query the set of 8 bits we’re interested in, and then we right shift them into an 8-bit value so we can print them back as hex values.
Summary
Summarizing how to set, clear, toggle, and query bit flags:
To query bit states, we use bitwise AND:
if (flags & option4) ... // if option4 is set, do something
To set bits (turn on), we use bitwise OR:
flags |= option4; // turn option 4 on.
flags |= (option4 | option5); // turn options 4 and 5 on.
To clear bits (turn off), we use bitwise AND with bitwise NOT:
flags &= ~option4; // turn option 4 off
flags &= ~(option4 | option5); // turn options 4 and 5 off
To flip bit states, we use bitwise XOR:
flags ^= option4; // flip option4 from on to off, or vice versa
flags ^= (option4 | option5); // flip options 4 and 5
Quiz time
Do not use std::bitset
in this quiz. We’re only using std::bitset
for printing.
Given the following program:
#include <bitset>
#include <cstdint>
#include <iostream>
int main()
[[maybe_unused]] constexpr std::uint8_t option_viewed{ 0x01 };
[[maybe_unused]] constexpr std::uint8_t option_edited{ 0x02 };
[[maybe_unused]] constexpr std::uint8_t option_favorited{ 0x04 };
[[maybe_unused]] constexpr std::uint8_t option_shared{ 0x08 };
[[maybe_unused]] constexpr std::uint8_t option_deleted{ 0x10 };
std::uint8_t myArticleFlags{ option_favorited };
// Place all lines of code for the following quiz here
std::cout << std::bitset<8>{ myArticleFlags } << '\n';
return 0;
a) Add a line of code to set the article as viewed.
Expected output:
00000101
Show Solution
myArticleFlags |= option_viewed;
b) Add a line of code to check if the article was deleted.
Show Solution
if (myArticleFlags & option_deleted) ...
c) Add a line of code to clear the article as a favorite.
Expected output (Assuming you did quiz (a)):
00000001
Show Solution
myArticleFlags &= static_cast<std::uint8_t>(~option_favorited);
If you got 00000000
, check two things:
That the solution for quiz (a) has not been removed.
That you cleared option_favorited
, not option_viewed
.
1d) Extra credit: why are the following two lines identical?
myflags &= ~(option4 | option5); // turn options 4 and 5 off
myflags &= ~option4 & ~option5; // turn options 4 and 5 off
Show Solution
De Morgan’s laws says that if we distribute a NOT, we need to flip ORs and ANDs to the other. So ~(option4 | option5)
becomes ~option4 & ~option5
.
Your email address will not be displayed
Find a mistake? Leave a comment above! Correction-related comments will be deleted after processing to help reduce clutter. Thanks for helping to make the site better for everyone!