Skip to content
Theory · Operators and Expressions · lesson 5 of 8

Bitwise operators, ternary and precedence

Settings such as "bold", "italic" and "underlined" take the values "yes" or "no". They are convenient to keep in one integer, one digit per setting. A number in memory is a row of binary digits, called bits, and each one is 0 or 1. Special operators read and change individual bits:

C++
#include <iostream>

int main() {
    unsigned a = 0b1100u;
    unsigned b = 0b1010u;
    std::cout << (a & b) << '\n';  // 8
    std::cout << (a | b) << '\n';  // 14
    std::cout << (a ^ b) << '\n';  // 6
    std::cout << (a & ~b) << '\n';  // 4
    std::cout << (a >> 2) << '\n';  // 3
    std::cout << (1u << 3) << '\n';  // 8
}

A literal with the 0b prefix writes a number in binary. Bits are numbered from right to left, starting at zero, and each bit is worth twice the one before it:

  • bit 0 is 1;
  • bit 1 is 2;
  • bit 2 is 4;
  • bit 3 is 8.

A number is the sum of its bits that are 1: 0b1100u is 8 + 4, that is 12, and 0b1010u is 8 + 2, that is 10.

Bitwise operators

Bitwise operators work on each bit separately, and &, | and ^ compare the bits with the same number in two numbers:

  • & (AND) gives 1 where the bit is 1 in both numbers: a & b is 0b1000u;
  • | (OR) gives 1 where the bit is 1 in at least one number: a | b is 0b1110u;
  • ^ (exclusive OR) gives 1 where the bit differs between the numbers: a ^ b is 0b0110u;
  • ~ (NOT) flips every bit, so a & ~b keeps only the bits of a that are not in b;
  • << and >> shift the bits left and right: a >> 2 is 0b0011u.

1u << n is a number with only bit n set. Shifting by as many bits as the type has, or more, is undefined behavior: for a 4-byte unsigned, that is a shift by 32 or more.

Such a number is called a mask. With it, flags | (1u << n) sets bit n, flags & ~(1u << n) clears it, and flags & (1u << n) tests it. Pick an operation and watch the mask set, clear or test bit n while the other bits stay as they were.

LiveMask: set, clear and test a bit(flags & mask) != 0 → true
unsigned flags = 0b00000110u;  // 6
unsigned mask = 1u << 2;       // 4
flags4 + 2 = 6

The low 8 bits of a 32-bit unsigned: the upper 24 bits of flags, mask and the result are 0

Only bit 2 of flags is left: the result is 4, so (flags & mask) != 0 is true.

Tap the bits of flags and change n

Bitwise operators are used on unsigned types. In a signed type the highest bit holds the sign of the number, and it is called the sign bit. ~ or << can change this bit, and the number turns negative.

The ternary operator

The ternary operator c ? a : b picks one of two values. If the condition c holds, the result is a, otherwise b, and only the chosen operand is evaluated:

C++
int x = 7;
int y = 4;
int bigger = x > y ? x : y;
std::cout << bigger << '\n';  // 7
std::cout << (x % 2 == 0 ? "even" : "odd") << '\n';  // odd

Precedence

Operator precedence sets the order of operations in an expression: as in math, * runs before +. The main operators, from high precedence to low:

  • !, ~, ++, -- and unary -;
  • *, /, %, then + and -, then << and >>;
  • <, <=, >, >=, then == and !=;
  • &, then ^, then |;
  • &&, then ||, then ?: and =.

Two traps follow from this:

  • a shift runs after addition, so 1 << n + 1 is 1 << (n + 1);
  • ?: sits near the very bottom, so in c ? a : b + 1 the 1 is added only to b.

The << of std::cout is the same operator, with the same precedence. That is why a & b and ?: are in parentheses in the examples above. Without them, std::cout << a would group first, and the line would not compile. When the order is in doubt, add parentheses.

Common mistake

A bit is tested with a comparison and no parentheses:

C++
#include <iostream>

int main() {
    unsigned flags = 0b0110u;
    bool has_bit_2 = flags & 4u == 4u;  // ✗
    std::cout << has_bit_2 << '\n';  // 0
}
TEXT
warning: & has lower precedence than ==; == will be evaluated first [-Wparentheses]

The compiler only warns. == runs before &: 4u == 4u gives true, that is 1, and flags & 1u is 0, even though bit 2 is set. Parentheses are needed: (flags & 4u) == 4u.

Key points

  • A number is the sum of its bits that are 1, and bit n is worth 1u << n.
  • | with a mask sets a bit, & ~ clears it, and & tests it.
  • c ? a : b evaluates only the chosen operand.
  • &, ^ and | bind weaker than ==, so a bit test goes in parentheses.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way