Search

3.8a — Bit flags and bit masks

By Alex on September 8th, 2015 | last modified by Alex on February 19th, 2018

Note: This is a tough lesson. If you find yourself stuck, you can safely skip this lesson and come back to it later.

Bit flags

The smallest addressable unit of memory is a byte. Since all variables need to have unique addresses, this means variables must be at least one byte in size.

For most variable types, this is fine. However, for boolean values, this is a bit wasteful. Boolean types only have two states: true (1), or false (0). This only requires one bit to store. However, if a variable must be at least a byte, and a byte is typically 8 bits, that means a boolean is using 1 bit and leaving the other 7 unused.

In the majority of cases, this is fine -- we’re usually not so hard-up for memory that we need to care about 7 wasted bits. However, in some storage-intensive cases, it can be useful to “pack” 8 individual boolean values into a single byte for storage efficiency purposes. This is done by using the bitwise operators to set, clear, and query individual bits in a byte, treating each as a separate boolean value. These individual bits are called bit flags.

Bit counting

When talking about individual bits, we typically count from right to left, starting with 0 (not 1). So given the bit pattern 0000 0111, bits 0 through 2 are 1, and bits 3 through 7 are 0.

So although we are typically used to counting starting with 1, in this lesson we’ll generally count starting from 0.

Defining bit flags in C++14

In order to work with individual bits, we need to have a way to identify the individual bits within a byte, so we can manipulate those bits (turn them on and off). This is typically done by defining a symbolic constant to give a meaningful name to each bit used. The symbolic constant is given a value that represents that bit.

Now we have a set of symbolic constants that represents each bit position. We can use these to manipulate the bits (which we’ll show how to do in just a moment).

Defining bit flags in C++11 or earlier

Because C++11 doesn’t support binary literals, we have to use other methods to set the symbolic constants. There are two good methods for doing this. Less comprehensible, but more common, is to use hexadecimal. If you need a refresher on hexadecimal, please revisit lesson 2.8 -- Literals.

So 0000 1100 gets assigned back to myflags. In other words, we just turned off bit 4 (and left the other bits alone).

We can turn off multiple bits at the same time:

1

myflags&=~(option4|option5);// turn options 4 and 5 off at the same time

Flipping individual bits

To toggle a bit state, we use bitwise XOR:

1

2

myflags^=option4;// flip option4 from on to off, or vice versa

myflags^=(option4|option5);// flip options 4 and 5 at the same time

Determining if a bit is on or off

To query a bit state, we use bitwise AND:

1

2

3

4

if(myflags&option4)

std::cout<<"myflags has option 4 set";

if!(myflags&option5)

std::cout<<"myflags does not have option 5 set";

Bit flags in real life

In OpenGL (a 3d graphics library), some functions take one or more bit flags as a parameter:

1

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 defined as follows (in gl2.h):

1

2

3

#define GL_DEPTH_BUFFER_BIT 0x00000100

#define GL_STENCIL_BUFFER_BIT 0x00000400

#define GL_COLOR_BUFFER_BIT 0x00004000

Here’s a less abstract example for a game we might write:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

#include <iostream>

intmain()

{

// Define a bunch of physical/emotional states

constunsignedcharisHungry=1<<0;// 0000 0001

constunsignedcharisSad=1<<1;// 0000 0010

constunsignedcharisMad=1<<2;// 0000 0100

constunsignedcharisHappy=1<<3;// 0000 1000

constunsignedcharisLaughing=1<<4;// 0001 0000

constunsignedcharisAsleep=1<<5;// 0010 0000

constunsignedcharisDead=1<<6;// 0100 0000

constunsignedcharisCrying=1<<7;// 1000 0000

unsignedcharme=0;// 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'll use static_cast<bool> to interpret the results as a boolean value rather than an integer)

std::cout<<"I am happy? "<<static_cast<bool>(me&isHappy)<<'\n';

std::cout<<"I am laughing? "<<static_cast<bool>(me&isLaughing)<<'\n';

return0;

}

Why are bit flags useful?

Astute readers will note that the above myflags example actually doesn’t save any memory. 8 booleans would normally take 8 bytes. But the above example uses 9 bytes (8 bytes to define the bit flag options, and 1 bytes for the bit flag)! So why would you actually want to use bit flags?

Bit flags are typically used in two cases:

1) When you have many sets of identical bitflags.

Instead of a single myflags variable, consider the case where you have two myflags variables: myflags1 and myflags2, each of which can store 8 options. If you defined these as two separate sets of booleans, you’d need 16 booleans, and thus 16 bytes. However, using bit flags, the memory cost is only 10 (8 bytes to define the options, and 1 byte for each myflags variable). With 100 myflag variables, your memory cost would be 108 bytes instead of 800. The more identical variables you need, the more substantial your memory savings.

Let’s take a look at a more concrete example. Imagine you’re creating a game where there are monsters for the player to fight. When a monster is created, it may be resistant to certain types of attacks (chosen at random). The different type of attacks in the game are: poison, lightning, fire, cold, theft, acid, paralysis, and blindness.

In order to track which types of attacks the monster is resistant to, we can use one boolean value per resistance (per monster). That’s 8 booleans per monster.

With 100 monsters, that would take 800 boolean variables, using 800 bytes of memory.

However, using bit flags:

1

2

3

4

5

6

7

8

constunsignedcharresistsPoison=1<<0;

constunsignedcharresistsLightning=1<<1;

constunsignedcharresistsFire=1<<2;

constunsignedcharresistsCold=1<<3;

constunsignedcharresistsTheft=1<<4;

constunsignedcharresistsAcid=1<<5;

constunsignedcharresistsParalysis=1<<6;

constunsignedcharresistsBlindness=1<<7;

Using bit flags, we only need one byte to store the resistances for a single monster, plus a one-time setup fee of 8 bytes for the options.

With 100 monsters, that would take 108 bytes total, or approximately 8 times less memory.

For most programs, the amount of memory using bit flags saved 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.

2) 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:

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 parameters corresponds to which option (is setting the edit flag the 9th, 10th, or 11th parameter?) It may also not be very performant, as every function call has to copy 32 booleans from the caller to the function.

Instead, if you defined the function using bit flags like this:

1

voidsomeFunction(unsignedintoptions);

Then you could use bit flags to pass in only the options you wanted:

1

someFunction(option10|option32);

Not only is this much more readable, it’s likely to be more performant as well, since it only involves 2 operations (one bitwise OR and one parameter copy).

This is one of the reasons OpenGL opted to use bitflag parameters instead of many consecutive booleans.

Also, if you have unused bit flags and need to add options later, you can just define the bit flag. There’s no need to change the function prototype, which is good for backwards compatibility.

An introduction to std::bitset

All of this bit flipping is exhausting, isn’t it? Fortunately, the C++ standard library comes with functionality called std::bitset that helps us manage bit flags.

To create a std::bitset, you need to include the bitset header, and then define a std::bitset variable indicating how many bits are needed. The number of bits must be a compile time constant.

1

2

3

#include <bitset>

std::bitset<8>bits;// we need 8 bits

If desired, the bitset can be initialized with an initial set of values:

1

2

3

4

#include <bitset>

std::bitset<8>bits(option1|option2);// start with option 1 and 2 turned on

std::bitset<8>morebits(0x3);// start with bit pattern 0000 0011

Note that our initialization value is interpreted as binary. Since we pass in the value 3, the std::bitset will start with the binary value for 3 (0000 0011).

std::bitset provides 4 key functions:

test() allows us to query whether a bit is a 0 or 1

set() allows us to turn a bit on (this will do nothing if the bit is already on)

reset() allows us to turn a bit off (this will do nothing if the bit is already off)

flip() allows us to flip a bit from a 0 to a 1 or vice versa

Each of these functions takes a bit-position parameter indicating which bit should be operated on. The position of the rightmost bit is 0, increasing with each successive bit to the left. Giving descriptive names to the bit indices can be useful here (either by assigning them to const variables, or using enums, which we’ll introduce in the next chapter).

Note that sending the bitset variable to std::cout prints the value of all the bits in the bitset.

Remember that the initialization value for a bitset is treated as binary, whereas the bitset functions use bit positions!

std::bitset also supports the standard bit operators (operator|, operator&, and operator^), so you can still use those if you wish (they can be useful when setting or querying multiple bits at once).

We recommend using std::bitset instead of doing all the bit operations manually, as bitset is more convenient and less error prone.

(h/t to reader “Mr. D”)

Bit masks

The principles for bit flags can be extended to turn on, turn off, toggle, or query multiple bits at once, in a bit single operation. When we bundle individual bits together for the purpose of modifying them as a group, this is called a bit mask.

Let’s take a look at a sample program using bit masks. In the following program, we ask the user to enter a number. We then use a bit mask to keep only the low 4 bits, which we print the value of.

Although this example is pretty contrived, the important thing to note is that we modified multiple bits in one operation!

An RGBA color example

Now lets take a look at a more complicated example.

Color display devices such as TVs and monitors are composed of millions of pixels, each of which can display a dot of color. The dot of color is composed from three beams of light: one red, one green, and one blue (RGB). By varying the intensity of the colors, any color on the color spectrum can be made. Typically, the amount 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

red

green

blue

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.

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 to move them to the range of 0-255 for storage and printing.

Note: RGBA is sometimes stored as ARGB instead, with the alpha channel being stored in the most significant byte rather than the least significant.

Summary

Summarizing how to set, clear, toggle, and query bit flags:

To query bit states, we use bitwise AND:

1

if(myflags&option4)...// if option4 is set, do something

To set bits (turn on), we use bitwise OR:

1

2

myflags|=option4;// turn option 4 on.

myflags|=(option4|option5);// turn options 4 and 5 on.

To clear bits (turn off), we use bitwise AND with an inverse bit pattern:

1

2

myflags&=~option4;// turn option 4 off

myflags&=~(option4|option5);// turn options 4 and 5 off

To toggle bit states, we use bitwise XOR:

1

2

myflags^=option4;// flip option4 from on to off, or vice versa

myflags^=(option4|option5);// flip options 4 and 5

Quiz

1) Given the following program:

1

2

3

4

5

6

7

8

9

10

11

12

intmain()

{

unsignedcharoption_viewed=0x01;

unsignedcharoption_edited=0x02;

unsignedcharoption_favorited=0x04;

unsignedcharoption_shared=0x08;

unsignedcharoption_deleted=0x80;

unsignedcharmyArticleFlags=0;

return0;

}

1a) Write a line of code to set the article as viewed.
1b) Write a line of code to check if the article was deleted.
1c) Write a line of code to clear the article as a favorite.
1d) Extra credit: why are the following two lines identical?

Explain about how they are just manipulators before you show how to create then, I got stuck there and didn't even read the rest... If I had read the whole page first I would have understood! Great tutorial btw 🙂
Edit: Put this example there, just to demonstrate the use of option:
myflags |= option4;
What it actually does is:
myflags = 0000 0000
option4 = 0001 0000
OR result: 0001 0000
So you effectively turned that on.

You seem to have some trouble with the binary system, a decimal 4 is 0100, not 00010000. 00010000 is 16.

Here's an example of how conversion between binary and decimal works.
Let's say we have the binary number 01010001.
The least significant (last) bit is decimal 1. The next bit is 2, then 4, then 8. Always multiply the previous value by 2 to get the next.
Write down those decimals values and write the binary number beneath.

1

2

DEC1286432168421

BIN01010001

Whenever there's a 1 in the binary number you take to decimal above and sum it up.
1 + 16 + 64 = 81
So binary 01010001 is decimal 81.

Thanks. Got it in the first place, I think I am asking other thing:
Let's say -

1

2

3

4

std::bitset<8>bits(0x1);

bits.set(option5);

std::cout<<"All the bits are: "<<bits<<"nn";

std::cout<<"Bit 4 has a value: "<<bits.test(option5)<<"nn";

Let me explain what I am getting from the 1st line of code till the last line I have written above; if you run this code it sets the byte size of 8 bits which we want to use which is binary 1(0000 0001)[using bits(0x1), then we set (means turn on) the 4th bit(option5) to 1 from 0.

3rd Line of Code prints all the bits like:

1

00010001

4th Line of Code says:

1

Bit4hasavalue:1

If you see the output here it says: Bit 4 has a value, but if we count the bits from the right it is 5th bit in the output module. This is my doubt with full explanation. Do rectify me where I am wrong; I know I am not right most of the time. But just wanted to ask.
Thanks

In light of this conversation, I've:
* Added a subsection talking about how we count bits
* Changed the option names from option1 through option8 to option0 through option7. This is slightly less intuitive for naming options, but aligns the option names with the bit numbers, which is probably an overall win.

Hi Will!
You're right. If you actually need 32 options then you'll need to declare 32 options.
Mind sharing what you need that many options for? There might be more convenient ways to handle them than a bitset.

where 'x' is a boolean expression.
Your exclamation marks are in between the 'if' and the '(', where there's not allowed to be anything but whitespace. The correct place to apply the negation is inside the brackets.

I thought I would revisit question 6 from section 3.7 and do it using what I have learnt in sections 3.8 and 3.8a. So here goes nothing 😉 I think my code my be a little inelegant, but I just wanted to play around with the concept of bit flags (which I find very cool). I don't really have a question here, just wanted to share my alternative answer to question 6 from section 3.7 here.

Hi Alex,
Could you show me how to use bitset to set on multiple bits at once? When I try using bitwise OR like bits.set(1|2), it turns to bits.set(3) so that only bit 3 is on. I'm a bit confused here 🙂 Thanks.

Thanks for the reply. But I still have some problems because of lack of bit level programming.Is n’t myflag value 00000000 and hence AND operation of myflag with anything would always evaluate to false (0).

Logical NOT (!) is typically used for boolean values. It will flip a 0 to a 1, or a non-zero number to a 0.
Bitwise NOT (~) is typically used for bit flags and bit masks. It will flip each individual bit from 0 to 1 or 1 to 0.