Bitwise Operations and Register Masks
Set, clear and test individual bits without confusing logical and bitwise operators.
A mask is a stencil laid over a row of switches. It selects which positions an operation is allowed to affect.
Bitwise AND, OR and XOR operate on corresponding bit positions. Logical AND and OR instead combine truth values and can short-circuit. For example, 2 & 1 is zero because the set bits do not overlap, while 2 && 1 is true because both values are nonzero. Confusing the two changes the meaning of a condition.
To select bit three, counting from zero, form the mask 1 shifted left by three. OR with the mask sets that bit; AND with the inverted mask clears it; XOR toggles it. AND with the mask tests whether it is set. Use unsigned types and a shift count smaller than the operand width to avoid invalid or surprising behavior.
The example uses an ordinary RAM variable, not a hardware address. A peripheral register may have read-only bits, write-one-to-clear flags or read side effects. For such a register, a read-modify-write expression can accidentally clear or change something else. Consult the register specification and use dedicated set/clear registers when provided.
Concurrency also matters: an interrupt or another execution context can update the same value between the read and write. A correct bit mask does not make the entire operation atomic. Use the chip or platform's appropriate atomic register operation or synchronization mechanism when the value is shared.
Set, clear and test individual bits without confusing logical and bitwise operators.
Core ideas
- Bitwise & differs from logical &&.
- Check bit numbering and operand width.
- Hardware register access must follow its documented semantics.
Set: value | maskClear: value & ~maskTest: (value & mask) != 0Work through a practical example
1Start with value 0x02, binary 00000010.
2Set bit three using mask 0x08: result 0x0A.
3Clear bit one using mask 0x02: result 0x08.
4Test bit three: the masked result is nonzero, so it is set.
Using a generic read-modify-write sequence on a write-one-to-clear status register.
Code example
C++17 — standalone RAM example; expected output: 8 1
#include <cstdint>
#include <cstdio>
int main() {
std::uint32_t flags = 0x02u;
flags |= (std::uint32_t{1} << 3);
flags &= ~(std::uint32_t{1} << 1);
const bool set = (flags & (std::uint32_t{1} << 3)) != 0;
std::printf("%lu %d\n", static_cast<unsigned long>(flags), set);
}Read and run this example in the stated environment. Code is displayed here; it does not execute in your browser.