Bitmasks

Bitmasks, and the bitwise operators, are something I've known about/can recognise, but had never actually needed to use them. The first time was when using Windows C++ APIs, and I tripped up over myself a few times trying to use it... So, here's what I wish I read at the start.

tldr

  • Add flags using OR |:
    • flags |= BORDER_LEFT
    • flags |= BORDER_LEFT | BORDER_RIGHT
  • Remove flags using AND & with NOT ~:
    • flags &= ~BORDER_LEFT
    • flags &= ~(BORDER_LEFT | BORDER_RIGHT)
  • Toggle flags using XOR ^:
    • flags ^= BORDER_LEFT
    • flags ^= BORDER_LEFT | BORDER_RIGHT
  • Check any flag using AND &:
    • if (flags & BORDER_LEFT)
    • toCheck = BORDER_LEFT | BORDER_RIGHT; if ((flags & toCheck) != 0)
    • the result is non-0 if any of the flags match
  • Check all flags using AND &:
    • toCheck = BORDER_LEFT | BORDER_RIGHT; if ((flags & toCheck) == toCheck)

See in Repl.it

Flags

You'll often see bitmasks to used for "flags", for example:

BORDER_LEFT   = 0b0001 // 0b is the prefix for a binary number  
BORDER_RIGHT  = 0b0010  
BORDER_TOP    = 0b0100  
BORDER_BOTTOM = 0b1000  

OR (|)

The | (OR) operator sets a bit to 1 if either of the bits in that position are 1. We can use this to combine flags together:

BORDER_VERTICAL   = BORDER_TOP | BORDER_BOTTOM  
//   0100
// | 1000
// = 1100

BORDER_HORIZONTAL = BORDER_LEFT | BORDER_RIGHT  
//   0001
// | 0010
// = 0011

BORDER_ALL = BORDER_VERTICAL | BORDER_HORIZONTAL  
//   1100
// | 0011
// = 1111

myBorders = BORDER_HORIZONTAL | BORDER_BOTTOM  
//   0011
// | 1000
// = 1011

AND (&)

The & (AND) operator sets a bit to 1 if both bits in that position are 1. We can use this to check a flag:

flags = BORDER_TOP | BORDER_HORIZONTAL  
// 0111

hasTopBorder = flags & BORDER_TOP  
//   0111
// & 0100
// = 0100

hasBottomBorder = flags & BORDER_BOTTOM  
//   0111
// & 1000
// = 0000

See hasTopBorder becomes truthy (it's 0b100, which is 4 in decimal, which is truthy), and hasBottomBorder becomes falsey (it's 0b0 which is.. 0).

NOT (~)

The ~ (NOT) operator flips the bits in each position. Anything which was 0 is now 1, and anything which was 1 is now 0.

BORDER_BOTTOM  
// 1000

~BORDER_BOTTOM
// 0111

~~BORDER_BOTTOM
// 1000

We can use this, alongside the & operator to remove a flag:

flags = BORDER_TOP & BORDER_RIGHT & BORDER_LEFT  
// 0111

flagToRemove = BORDER_TOP  
// 0100

flags = flags & ~flagToRemove  
//   0111
// & 1011   <-- NOT (flip) of 0100
// = 0011

We've removed BORDER_TOP from the bits.

XOR (^)

The ^ (XOR) operator sets a bit if both bits in that position are different (i.e. only one side is 1). This can be used for toggling flags:

flags = BORDER_BOTTOM | BORDER_LEFT | BORDER_RIGHT  
//   1000
// | 0001
// | 0010
// = 1011

flags = flags ^ BORDER_LEFT  
//   1011
// ^ 0001
// = 1010

flags = flags ^ BORDER_LEFT  
//   1010
// ^ 0001
// = 1011

Prefer videos?

Check out this video by The Cherno on YouTube:

Posted on
Written by Dan Harper
Tagged under