K&R C Exercise 2-4: squeeze — Delete Characters from a String

Exercise 2-4. Write an alternative version of squeeze(s1,s2) that deletes each character in s1 that matches any character in the string s2.

Approach

The original K&R squeeze(s, c) removes all occurrences of a single character c from string s using a compact two-index pattern: index i scans every position in s, while index j only advances when a character is worth keeping. This exercise generalises that idea — instead of comparing against one character, you search an entire string s2 for each character of s1.

The key insight is the inner loop’s exit condition. The loop over s2 terminates in exactly two ways: either it found the character (s2[k] == s1[i]) or it ran off the end of s2 (s2[k] == '\0'). After the loop you test which case occurred: s2[k] == '\0' means the character was not in s2, so it survives. This “loop-then-test” idiom is a recurring pattern in C string handling — it appears in strchr, strpbrk, and countless hand-rolled searches.

Solution

/* compile: gcc -ansi -Wall ex2-4.c -o ex2-4 */
#include <stdio.h>

void squeeze(char s1[], const char s2[]);

int main(void)
{
    char s1[] = "hello, world!";
    char s2[] = "aeiou";

    printf("Before: %s\n", s1);
    squeeze(s1, s2);
    printf("After:  %s\n", s1);

    char t1[] = "programming";
    char t2[] = "mg";
    printf("Before: %s\n", t1);
    squeeze(t1, t2);
    printf("After:  %s\n", t1);

    return 0;
}

/* squeeze: delete all chars in s1 that appear in s2 */
void squeeze(char s1[], const char s2[])
{
    int i, j, k;

    for (i = j = 0; s1[i] != '\0'; ++i) {
        /* scan s2 looking for s1[i] */
        for (k = 0; s2[k] != '\0' && s2[k] != s1[i]; ++k)
            ;
        if (s2[k] == '\0')   /* not found in s2 — keep it */
            s1[j++] = s1[i];
    }
    s1[j] = '\0';
}

How It Works — Step by Step

Take s1 = "hello" and s2 = "aeiou":

  • i=0, s1[i]=’h’ — inner loop scans s2: ‘a’≠’h’, ‘e’≠’h’, ‘i’≠’h’, ‘o’≠’h’, ‘u’≠’h’, hits '\0'. Not in s2s1[j++]='h'.
  • i=1, s1[i]=’e’ — inner loop: ‘a’≠’e’, ‘e’==’e’, stops. s2[k] is 'e', not '\0' → found in s2, discard.
  • i=2, s1[i]=’l’ — not in s2 → kept.
  • i=3, s1[i]=’l’ — not in s2 → kept.
  • i=4, s1[i]=’o’ — found in s2 → discarded.
  • After loop: s1[j] = '\0' terminates the compacted string → "hll".

The two write-heads (i advancing unconditionally, j advancing only on a keep) compact the array in place without any extra buffer. This is the classic read/write pointer idiom for in-place filtering of arrays.

Compile and Run

gcc -ansi -Wall ex2-4.c -o ex2-4
./ex2-4

Sample Output

Before: hello, world!
After:  hll, wrld!
Before: programming
After:  rorai

What This Exercise Teaches

  • Two-index in-place filtering: using a read index i and a write index j to compact an array without extra memory — a pattern that appears everywhere in C string and array processing.
  • Loop-then-test search idiom: letting the inner loop run to exhaustion, then checking why it stopped. If s2[k] == '\0', the character was absent; if s2[k] == s1[i], it was found. This is exactly how strchr works internally.
  • Generalising single-argument to multi-argument: the only structural change from the original squeeze(s, c) is wrapping the comparison in a loop over s2. Spotting that the outer shell stays the same while the predicate changes is a key design skill.
  • Null-terminating after compaction: s1[j] = '\0' is not optional — without it the string retains stale characters past its logical end and produces undefined behaviour when passed to any string function.

Relation to the Standard Library

What squeeze does manually, the standard library approximates through two functions declared in <string.h>:

  • strchr(s, c) — searches s for a single character c; returns a pointer to it or NULL.
  • strpbrk(s1, s2) — returns a pointer to the first character in s1 that also appears anywhere in s2, or NULL.

You could rewrite the inner loop as if (!strchr(s2, s1[i])), or advance i through the string using strpbrk. The manual version shown above makes the mechanics explicit, which is exactly what K&R intends at this stage of the book.

Set Up Your C Environment

To compile and run this solution you need GCC installed. If you haven’t set up C on your machine yet:

← Exercise 2-3  | 
Chapter 2 Solutions  | 
Exercise 2-5 →

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

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>