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:
#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: 0b1100u is 12, and 0b1010u is 10. Bits are numbered from right to left, starting at zero. Bit 0 stands for 1, bit 1 for 2, and bit 3 for 8.
Bitwise operators
Bitwise operators work on each pair of bits separately:
&(AND) gives 1 where the bit is 1 in both numbers:a & bis0b1000u;|(OR) gives 1 where the bit is 1 in at least one number:a | bis0b1110u;^(exclusive OR) gives 1 where the bit differs between the numbers:a ^ bis0b0110u;~(NOT) flips every bit, soa & ~bkeeps only the bits ofathat are not inb;<<and>>shift the bits left and right:a >> 2is0b0011u.
1u << n is a number with only bit n set. Shifting by as many positions as the type has bits, or more, is undefined behavior: for a 4-byte unsigned, that is 32. Bitwise operators are used on unsigned types. On signed ones, ~ and << can change the sign 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:
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'; // oddPrecedence
The order of operations in an expression is set by operator precedence: 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). And ?: 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:
#include <iostream>
int main() {
unsigned flags = 0b0110u;
bool has_bit_2 = flags & 4u == 4u; // ✗
std::cout << has_bit_2 << '\n'; // 0
}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.