C string algorithms

Write strlen and strcmp by hand, check palindromes, and count occurrences with strstr.

C
#include <ctype.h>
#include <stdio.h>
#include <string.h>

static size_t my_strlen(const char *s) {
  size_t n = 0;
  while (s[n] != '\0') {
    n++;
  }
  return n;
}

static int my_strcmp(const char *a, const char *b) {
  while (*a != '\0' && *a == *b) {
    a++;
    b++;
  }
  return (unsigned char)*a - (unsigned char)*b;
}

static int is_palindrome(const char *s) {
  int i = 0;
  int j = (int)strlen(s) - 1;
  while (i < j) {
    if (!isalnum((unsigned char)s[i])) {
      i++;
    } else if (!isalnum((unsigned char)s[j])) {
      j--;
    } else if (tolower((unsigned char)s[i]) != tolower((unsigned char)s[j])) {
      return 0;
    } else {
      i++;
      j--;
    }
  }
  return 1;
}

static int count_occurrences(const char *text, const char *pattern) {
  int count = 0;
  const char *p = text;
  while ((p = strstr(p, pattern)) != NULL) {
    count++;
    p++;
  }
  return count;
}

int main(void) {
  printf("%zu %zu\n", my_strlen("Programonic"), my_strlen(""));
  printf("%d %d\n", my_strcmp("apple", "apple") == 0, my_strcmp("apple", "banana") < 0);
  printf("%d %d\n", is_palindrome("A man, a plan, a canal: Panama"), is_palindrome("hello"));
  printf("%d\n", count_occurrences("abababa", "aba"));
  return 0;
}
Output
11 0
1 1
1 0
3

Explanation

A C string is an array of char ended by a zero byte. Functions such as strlen simply walk the array until they find that zero, so they take O(n) time, and calling strlen in a loop condition repeats that work on every pass. strcmp compares two strings byte by byte and returns a negative, zero, or positive number.

A palindrome check uses two indexes that move toward each other from both ends, skipping characters that are not letters or digits and comparing the rest without regard to case. strstr finds the first occurrence of one string inside another. Calling it repeatedly from just after each match counts every occurrence, including overlapping ones if the search restarts one character after the previous match.

Explore the dry run
Guided walkthroughReady
1#include <ctype.h>
2#include <stdio.h>
3#include <string.h>
4
5static size_t my_strlen(const char *s) {
6 size_t n = 0;
7 while (s[n] != '\0') {
8 n++;
9 }
10 return n;
11}
12
13static int my_strcmp(const char *a, const char *b) {
14 while (*a != '\0' && *a == *b) {
15 a++;
16 b++;
17 }
18 return (unsigned char)*a - (unsigned char)*b;
19}
20
21static int is_palindrome(const char *s) {
22 int i = 0;
23 int j = (int)strlen(s) - 1;
24 while (i < j) {
25 if (!isalnum((unsigned char)s[i])) {
26 i++;
27 } else if (!isalnum((unsigned char)s[j])) {
28 j--;
29 } else if (tolower((unsigned char)s[i]) != tolower((unsigned char)s[j])) {
30 return 0;
31 } else {
32 i++;
33 j--;
34 }
35 }
36 return 1;
37}
38
39static int count_occurrences(const char *text, const char *pattern) {
40 int count = 0;
41 const char *p = text;
42 while ((p = strstr(p, pattern)) != NULL) {
43 count++;
44 p++;
45 }
46 return count;
47}
48
49int main(void) {
50 printf("%zu %zu\n", my_strlen("Programonic"), my_strlen(""));
51 printf("%d %d\n", my_strcmp("apple", "apple") == 0, my_strcmp("apple", "banana") < 0);
52 printf("%d %d\n", is_palindrome("A man, a plan, a canal: Panama"), is_palindrome("hello"));
53 printf("%d\n", count_occurrences("abababa", "aba"));
54 return 0;
55}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Walk until the terminating zero byte: the length is the number of steps.—
—
The first differing bytes decide the order. Cast to unsigned char so that values above 127 compare correctly.—
—
Programonic has 11 characters and the empty string has none.—
11 0
Equal strings compare as 0, and apple sorts before banana.—
11 0
1 1
Compare letters case-insensitively once both ends point at letters or digits.—
11 0
1 1
The famous sentence reads the same backwards when punctuation and case are ignored.—
11 0
1 1
1 0
Restart one character after the previous match, so overlapping matches are counted.—
11 0
1 1
1 0
aba appears at positions 0, 2, and 4, which overlap.—
11 0
1 1
1 0
3

Cost and common mistakes

my_strlen and my_strcmp are O(n); count_occurrences is O(n·m) in the worst case.

  • Calling strlen(s) in the condition of a loop over s makes the loop O(n²). Compute the length once.
  • strcmp results should only be compared with 0, or tested for sign. Do not rely on the exact value.
  • Passing a negative char to isalnum or tolower is undefined behaviour, so cast to unsigned char first.

Try it yourself

Why does the example print 1 for my_strcmp("apple", "apple") == 0?

Reveal answer and reasoning

The comparison expression is true, which C represents as the integer 1. The two strings are identical, so strcmp returns 0.

Further reading