C · 2026-06-16 · embedded · bitwise · bit-manipulation · register
Bitwise operations (C)
Bit twiddling is half of register-level firmware, so it pays to use a vocabulary that is
correct by construction. Two rules cover most of the bugs: always work on unsigned
types — shifting a 1 into the sign bit of a signed int is undefined behaviour — and
parenthesise every macro argument, because & binds looser than ==.
#include <stdint.h>
#include <stdbool.h>
/* Canonical single-bit vocabulary. The 1u is unsigned on purpose; the extra
parentheses survive any expression passed as w or n. */
#define BIT(n) (1u << (n))
#define BIT_SET(w, n) ((w) |= BIT(n))
#define BIT_CLEAR(w, n) ((w) &= ~BIT(n))
#define BIT_TOGGLE(w, n) ((w) ^= BIT(n))
#define BIT_TEST(w, n) (((w) >> (n)) & 1u)
Multi-bit fields and useful idioms
Single bits are the easy case. A register field a few bits wide is a read-modify-write:
mask the old field out, OR the new value in. The constant-time tricks below all lean on one
identity — word & (word - 1) clears the lowest set bit.
/*******************************************************************************
* Function Name : bitfield_set
* Description : Replace a width-bit field at bit pos, leaving the rest intact.
* Input : word - original value; pos - field LSB; width - field size;
* value - new field value.
* Output : None.
* Return : word with the field updated.
*******************************************************************************/
static inline uint32_t bitfield_set(uint32_t word, uint32_t pos, uint32_t width, uint32_t value)
{
uint32_t mask = ((1u << width) - 1u) << pos;
return (word & ~mask) | ((value << pos) & mask);
}
/*******************************************************************************
* Function Name : bitfield_get
* Description : Extract a width-bit field at bit pos, right-aligned to bit 0.
* Input : word - source value; pos - field LSB; width - field size.
* Output : None.
* Return : The field value shifted down to bit 0.
*******************************************************************************/
static inline uint32_t bitfield_get(uint32_t word, uint32_t pos, uint32_t width)
{
return (word >> pos) & ((1u << width) - 1u);
}
/*******************************************************************************
* Function Name : count_set_bits
* Description : Population count via Kernighan's method — one pass per set bit.
* Input : word - value to inspect.
* Output : None.
* Return : Number of bits set to 1.
*******************************************************************************/
static inline uint32_t count_set_bits(uint32_t word)
{
uint32_t count = 0u;
while (word != 0u)
{
word &= (word - 1u); /* clear the lowest set bit */
count++;
}
return count;
}
/*******************************************************************************
* Function Name : is_power_of_two
* Description : True when exactly one bit is set (and word is non-zero).
* Input : word - value to test.
* Output : None.
* Return : true if a power of two, false otherwise.
*******************************************************************************/
static inline bool is_power_of_two(uint32_t word)
{
return (word != 0u) && ((word & (word - 1u)) == 0u);
}
Gotchas
- Precedence.
if (reg & MASK == 0)does not test what it looks like —==binds tighter than&, so it parses asreg & (MASK == 0). Writeif ((reg & MASK) == 0u). - Signed shifts.
1 << 31overflows a 32-bit signedint(UB);1u << 31is fine. Right-shifting a negative value is implementation-defined — keep everything unsigned. - Width 32.
(1u << 32)is undefined, so the field helpers above assumewidth < 32; for a full-word mask use0xFFFFFFFFudirectly. - Let the hardware help. For population count and bit scanning, most Cortex-M cores have
CLZ/RBIT;__builtin_popcountand__builtin_clzmap straight to them when available and beat the portable loop. Use the loop only where the intrinsic isn’t.