K&R C Exercise 2-7 — invert(x, p, n)
Exercise 2-7: Write a function
invert(x,p,n)that returnsxwith thenbits that begin at positionpinverted (i.e., 1 changed to 0 and vice versa), leaving the others unchanged.
Approach
XOR is the natural bit-flip operator: b ^ 1 = ~b and b ^ 0 = b. To flip exactly the bits at positions p down to p+1-n and leave everything else alone, XOR with a mask that has 1s at those positions and 0s everywhere else.
The mask is the same positioned mask used in Exercise 2-6 (setbits):
unsigned mask = ~(~0U << n); /* n ones at low-order positions */
int shift = p + 1 - n;
result = x ^ (mask << shift);
One line. Compare that to setbits, which needed two operations (clear then insert). invert is simpler because XOR handles both the clearing and the setting in a single pass — a 0-bit is set to 1, and a 1-bit is set to 0, all at once.
Worked example (8-bit illustration)
x = 0xF0 = 1111 0000
p = 5, n = 4
mask = ~(~0U << 4) = 0000 1111
shift = 5 + 1 - 4 = 2
mask << shift = 0000 1111 << 2 = 0011 1100 = 0x3C
x ^ (mask << shift) = 1111 0000
^ 0011 1100
= 1100 1100 = 0xCC
Bits 5, 4, 3, 2 of x (which were 1, 1, 0, 0) are now 0, 0, 1, 1. Bits 7, 6, 1, 0 are unchanged.
C Source Code
/* K&R Exercise 2-7 — invert
* Compile: gcc -ansi -Wall ex2-7.c -o ex2-7 */
#include <stdio.h>
unsigned invert(unsigned x, int p, int n);
int main(void)
{
printf("invert(0xF0, 5, 4) = 0x%02X\n", invert(0xF0, 5, 4));
printf("invert(0xFF, 3, 4) = 0x%02X\n", invert(0xFF, 3, 4));
printf("invert(0x00, 7, 8) = 0x%02X\n", invert(0x00, 7, 8));
return 0;
}
unsigned invert(unsigned x, int p, int n)
{
return x ^ (~(~0U << n) << (p + 1 - n));
}
Compile and Run
gcc -ansi -Wall ex2-7.c -o ex2-7 && ./ex2-7
Sample Output
invert(0xF0, 5, 4) = 0xCC invert(0xFF, 3, 4) = 0xF0 invert(0x00, 7, 8) = 0xFF
The second call inverts bits 3–0 of 0xFF: 11111111 ^ 00001111 = 11110000 = 0xF0. The third call inverts all eight bits of 0x00 starting at bit 7: 00000000 ^ 11111111 = 0xFF.
What This Exercise Teaches
- XOR as a bit-flip operator — XOR with 1 flips a bit; XOR with 0 leaves it alone. This is the fundamental mechanism behind checksums, RAID parity, and Feistel cipher rounds.
- Mask construction is the same as
setbits— the positioned mask~(~0U << n) << (p+1-n)appears in both exercises. Recognising shared sub-patterns is how you build a mental toolkit for bit manipulation. - invert vs. setbits —
setbitsreplaces bits (requires clear then insert: two operations).invertflips bits (XOR: one operation). When the desired result depends only on the current value of the bits — not on an external source — XOR is always the right tool. - Single-expression elegance — the entire function body is one return statement. That’s achievable because XOR is self-inverse and needs no separate clear step.
- ~0U for portable masks — same reasoning as Exercise 2-6: unsigned avoids undefined behaviour when left-shifting.
Set Up Your C Environment
You can compile and run this code on any system with GCC. If you haven’t set up C yet for Chapter 2:
- Install GCC on Windows
- Install GCC on macOS
- Install GCC on Linux
- VS Code for C Programming — recommended editor with integrated terminal
Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie