The symbols &, | and ^ become predictable when operands are aligned bit by bit, and & is easy to confuse with &&. Run a complete C program with 44 and 23, then use flags, packed fields and fixed-answer exercises. Displays show the low 8 bits, while the objects are unsigned int.
What bitwise operations do to individual bits
A bitwise operation applies a rule at each aligned position. Use 44u = 00101100 and 23u = 00010111, shown as low-byte 8-bit views. Review Number Systems and Base Conversions Explained if needed.
Operator | Name | Rule |
|---|---|---|
| AND | The result bit is 1 only for |
| OR | The result bit is 1 when either input is 1 |
| XOR | The result bit is 1 when the inputs differ |
| Complement | Flips every bit |
| Left shift | Moves bits left |
| Right shift | Moves bits right |
The one-bit truth table makes the first three rules precise:
A | B | A AND B | A OR B | A XOR B |
|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
Bitwise operators transform patterns; logical operators test truth. 6u & 3u gives 110 & 011 = 010, or 2. 6 && 3 sees two nonzero operands and gives 1. See C Programming & Data Structures for the wider C foundation.
Work every operator by hand with 44 and 23
Align the low 8 bits and apply one rule to each column:
x 00101100 (44)
y 00010111 (23)
x & y 00000100 (4)
x | y 00111111 (63)
x ^ y 00111011 (59)AND keeps bit 2 because both inputs contain 1. OR sets bit 5 because 1 | 0 = 1. XOR sets bit 0 because 0 ^ 1 = 1.
(~44u) & 0xFFu = 11010011 = 211; the mask selects the low byte, while plain ~44u covers the full unsigned int. Also, 44u << 2 = 10110000 = 176 and 44u >> 2 = 00001011 = 11. No set bit is discarded beyond this view.

Run the complete C program and match every output
Standard C has no portable %b, so use a helper:
#include <stdio.h>
static void print8(unsigned value) {
for (int bit = 7; bit >= 0; --bit) {
putchar((value & (1u << bit)) ? '1' : '0');
}
}
static void show(const char *label, unsigned value) {
printf("%-8s = ", label);
print8(value & 0xFFu);
printf(" (%u)\n", value & 0xFFu);
}
int main(void) {
unsigned x = 44u;
unsigned y = 23u;
show("x", x);
show("y", y);
show("x & y", x & y);
show("x | y", x | y);
show("x ^ y", x ^ y);
show("~x", (~x) & 0xFFu);
show("x << 2", x << 2);
show("x >> 2", x >> 2);
return 0;
}Compile with cc -std=c17 -Wall -Wextra bitwise.c -o bitwise, then run ./bitwise:
x = 00101100 (44)
y = 00010111 (23)
x & y = 00000100 (4)
x | y = 00111111 (63)
x ^ y = 00111011 (59)
~x = 11010011 (211)
x << 2 = 10110000 (176)
x >> 2 = 00001011 (11)The u suffix makes operands unsigned. value & 0xFFu limits display to positions 7 through 0, not the width of unsigned int.
Use masks to set, clear, toggle and test flags
Use four permission flags, displayed as SHARE EXECUTE WRITE READ.
#define READ (1u << 0)
#define WRITE (1u << 1)
#define EXECUTE (1u << 2)
#define SHARE (1u << 3)From flags = 0000, flags |= READ | WRITE gives 0011 (3). (flags & WRITE) != 0u is true because the mask returns 0010 (2). Next, flags ^= WRITE gives 0001 (1), flags |= EXECUTE gives 0101 (5), and flags &= ~READ gives 0100 (4).
OR sets selected bits, AND with a complemented mask clears them, XOR toggles them, and AND without assignment tests them.
Pack and extract three fields in one byte
Bits 0 to 2 store priority (0 to 7), bits 3 to 4 store mode (0 to 3), bit 5 stores enabled, and bits 6 to 7 are unused. Pack 5, 2 and 1 as:
unsigned packed = (5u & 0x7u)
| ((2u & 0x3u) << 3)
| ((1u & 0x1u) << 5);The contributions are 00000101 (5), 00010000 (16) and 00100000 (32). OR produces 00110101 = 0x35 = 53. Extract with packed & 0x7u = 5, (packed >> 3) & 0x3u = 2, and (packed >> 5) & 0x1u = 1.

Apply two useful bit patterns and trace them
For unsigned n, test a positive power of two with n != 0u && (n & (n - 1u)) == 0u. For 32, 00100000 & 00011111 = 00000000, true. For 40, 00101000 & 00100111 = 00100000 (32), false. Subtracting one flips the lowest set bit and all lower bits.
The same observation counts set bits efficiently:
while (value != 0u) {
value &= value - 1u;
++count;
}For 44: 44 (00101100) -> 40 (00101000) -> 32 (00100000) -> 0 (00000000). Three iterations mean three set bits. Also, 0xB4u & 0x0Fu = 0x04u, so the low nibble is 4.
Avoid precedence, width and signed-shift traps
Do not confuse
6u & 3u = 2with6 && 3 = 1.Parenthesise before comparison. For
flags = 0uandmask = 4u,(flags & mask) == 0uis true.flags & mask == 0uparses asflags & (mask == 0u)and gives 0.Do not treat
~as an eight-bit operator. Only(~44u) & 0xFFugives the fixed low-byte result 211.Keep masks and shifts unsigned. An unrepresentable signed left shift is undefined, while right-shifting a negative signed value is implementation-defined.
Never use a negative shift count or one greater than or equal to the promoted left operand's width. Such a count is undefined.
Remember that standard C does not define
%b; the helper above is intentional.
Practise the question forms, then take the next step
Write aligned binary rows before checking these answers:
0xB4u & 0x0Fugives0x04u(4).0xB4u | 0x02usets bit 1 and gives0xB6u(182).0xB4u ^ 0x80utoggles bit 7 and gives0x34u(52).(0x35u >> 3) & 0x3uextracts mode and gives 2.
Also practise choosing masks, repairing precedence bugs, tracing shifts, extracting fields, checking powers of two and counting set bits.
The short version is: AND selects, OR sets, XOR toggles, complement flips, and shifts reposition. Masks make each operation selective. Continue with the C Language Course: Concepts, MCQs & Coding, try the focused C Programming Course, or compare broader paths in Coding & Skills.




