toupper and tolower in C: solved exercise

toupper and tolower in C: solved exercise

If you searched for a solved toupper and tolower exercise in C, here are the essential patterns: converting a single character, normalizing a full string to upper or lower case, and performing case-insensitive search.

toupper and tolower are declared in <ctype.h> and operate on an int (the character value cast to unsigned char). If the character is not a letter, it is returned unchanged.

Problem statement

  1. Convert the character 'a' to uppercase and 'Z' to lowercase.
  2. Normalize the string "Hello, WORLD! 123" to all lowercase and all uppercase.
  3. Compare two strings ignoring case without modifying the originals.

C solution

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <ctype.h>
#include <string.h>

void to_lower(const char *src, char *dst) {
    while (*src) *dst++ = (char)tolower((unsigned char)*src++);
    *dst = '\0';
}

void to_upper(const char *src, char *dst) {
    while (*src) *dst++ = (char)toupper((unsigned char)*src++);
    *dst = '\0';
}

/* Case-insensitive comparison without modifying originals */
int strcmp_ci(const char *a, const char *b) {
    while (*a && *b) {
        int da = tolower((unsigned char)*a);
        int db = tolower((unsigned char)*b);
        if (da != db) return da - db;
        a++; b++;
    }
    return tolower((unsigned char)*a) - tolower((unsigned char)*b);
}

int main(void) {
    /* 1. Single character */
    printf("toupper('a') = '%c'\n", toupper('a'));
    printf("tolower('Z') = '%c'\n", tolower('Z'));

    /* 2. Full string */
    const char *original = "Hello, WORLD! 123";
    char buf[64];

    to_lower(original, buf);
    printf("Lowercase: %s\n", buf);

    to_upper(original, buf);
    printf("Uppercase: %s\n", buf);

    /* 3. Case-insensitive comparison */
    const char *s1 = "Programming";
    const char *s2 = "PROGRAMMING";
    printf("strcmp_ci(\"%s\", \"%s\") = %d\n", s1, s2, strcmp_ci(s1, s2));

    return 0;
}

Expected output

1
2
3
4
5
toupper('a') = 'A'
tolower('Z') = 'z'
Lowercase: hello, world! 123
Uppercase: HELLO, WORLD! 123
strcmp_ci("Programming", "PROGRAMMING") = 0

Common mistakes

  • Passing char without casting to unsigned char: on platforms where char is signed, characters with code > 127 (accented letters) are represented as negative integers. The standard requires the argument to be unsigned char or EOF.
  • Modifying the original string when only comparison is needed: the correct approach is to convert to a copy or compare character by character as in strcmp_ci.
  • Assuming toupper/tolower works with all locales: the <ctype.h> functions depend on the active locale. They always work for basic ASCII letters (a-z, A-Z). For ñ, á, etc., use setlocale or internationalization functions.
  • Forgetting the \0 terminator when building the destination string: to_lower appends it explicitly; without it, printf("%s") reads beyond the buffer.

Practical use

Normalizing strings to a uniform case is used in text search, identifier comparison (shell commands, usernames), protocol token parsing, and any system where user input has no fixed case.

Guided practice and full book

If you want a complete path with progressive difficulty:

FAQ

Why must you cast to unsigned char before calling toupper/tolower?

Because the C standard specifies that the argument must be representable as unsigned char or must be EOF. On platforms where char is signed, a character with code > 127 becomes a negative integer, which causes undefined behavior when passed to toupper/tolower. The (unsigned char) cast normalizes the value to the correct range (0–255).

Do toupper and tolower modify the original string?

No. They are pure functions that take a character and return another. To modify a string you must iterate character by character and replace each element with the result, as to_upper does in this exercise.

How does normalization work with accented characters (á, é, ñ)?

The <ctype.h> functions work with the active locale (setlocale). In the default "C" locale they only handle ASCII letters. To support UTF-8 characters like á or ñ, use towupper/towlower from <wctype.h> with wchar_t types, or an internationalization library like ICU.