K&R C Exercise 2-7: invert — Flip n Bits at Position p

K&R C Exercise 2-7 — invert(x, p, n)

Exercise 2-7: Write a function invert(x,p,n) that returns x with the n bits that begin at position p inverted (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. setbitssetbits replaces bits (requires clear then insert: two operations). invert flips 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:

Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie


← Exercise 2-6
 | 
Chapter 2 Solutions
 | 
Exercise 2-8 →

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>