Skip to content

Beginner · Strings in C · topic 19 of 60 · 25 min

Characters and <ctype.h>

Explanation

A char is a small integer holding a character code; in ASCII, 'A' is 65, 'a' is 97 and '0' is 48. So c - '0' turns a digit character into its number. <ctype.h> provides isalpha, isdigit, isspace, isupper, toupper and tolower; pass them an unsigned char value. These tools power validation, counting and simple ciphers.

Characters are numbers

c
char c = 'A';
printf("%c %d\n", c, c);          /* A 65 */
printf("%c\n", c + 2);            /* C */
printf("%d\n", '7' - '0');        /* 7: the digit's value */
printf("%c\n", 'a' + ('Z' - 'A'));/* z */
CharactersASCII codes
'0' to '9'48 to 57
'A' to 'Z'65 to 90
'a' to 'z'97 to 122 (lower case = upper case + 32)
' ' (space)32
'\n' (newline)10
'\0' (terminator)0

Digits and letters are each in consecutive blocks in ASCII, which is why c - '0' and c - 'A' work. ASCII covers English only; Indian scripts are stored as multi-byte UTF-8 sequences, which plain char functions do not understand (each Devanagari character is several bytes).

<ctype.h> functions

c
#include <ctype.h>

int vowels = 0, digits = 0, spaces = 0;
const char *text = "Order 42 shipped to Asha";
for (const char *p = text; *p; p++) {
    unsigned char ch = (unsigned char) *p;
    if (isdigit(ch)) digits++;
    else if (isspace(ch)) spaces++;
    else if (strchr("aeiou", tolower(ch))) vowels++;
}
printf("%d vowels, %d digits, %d spaces\n", vowels, digits, spaces);   /* 7 vowels, 2 digits, 4 spaces */
FunctionTrue for
isalpha(c)letters
isdigit(c)'0'..'9'
isalnum(c)letters or digits
isspace(c)space, '\t', '\n', '\r', '\v', '\f'
isupper / islowerupper- / lower-case letters
ispunct(c)punctuation
toupper / tolowerreturn the converted character
Careful: The ctype functions take an int that must be either EOF or a value representable as unsigned char. On platforms where char is signed, a byte above 127 (for example from UTF-8 text) becomes negative, and passing it directly is undefined behaviour. Always cast: isdigit((unsigned char) c).

A Caesar cipher

c
void caesar(char *s, int shift) {
    shift = ((shift % 26) + 26) % 26;              /* handle negative shifts */
    for (; *s; s++) {
        unsigned char ch = (unsigned char) *s;
        if (isupper(ch)) *s = (char)('A' + (ch - 'A' + shift) % 26);
        else if (islower(ch)) *s = (char)('a' + (ch - 'a' + shift) % 26);
    }
}
/* "Chai, 2 cups" with shift 3 → "Fkdl, 2 fxsv" */
Classic interview exercise. (Real encryption uses proper cryptographic libraries, never home-made ciphers.)

Upper-case a string in place

c
void to_upper(char *s) {
    for (size_t i = 0; s[i] != '\0'; i++) s[i] = (char) toupper((unsigned char) s[i]);
}
Think of it like this: Character codes are like seat numbers in a cinema where the digits sit in row 48–57 and capital letters in row 65–90: knowing the first seat of a row lets you find any seat by counting (c - 'A').
Tip: Prefer isdigit/isalpha over hand-written ranges like c >= '0' && c <= '9'; they say what you mean and also respect the locale for letters.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Characters and <ctype.h> · 1/6

Characters and ctype

In this lesson you will work with individual characters.

Prefer a video by a teacher? Find free videos on “c ctype.h isdigit isalpha toupper ascii char arithmetic” on YouTube ↗

In real life: Validating a vehicle number plate

A parking app checks that a number plate typed by the attendant has the common Indian format: two letters (state), two digits (district), one or two letters, then four digits, like MH12AB1234. Simple ctype checks catch most typos before the plate is saved.

c
int looks_like_plate(const char *p) {
    size_t n = strlen(p);
    if (n < 9 || n > 10) return 0;
    if (!isupper((unsigned char)p[0]) || !isupper((unsigned char)p[1])) return 0;
    if (!isdigit((unsigned char)p[2]) || !isdigit((unsigned char)p[3])) return 0;
    for (size_t i = 4; i < n - 4; i++) if (!isupper((unsigned char)p[i])) return 0;
    for (size_t i = n - 4; i < n; i++) if (!isdigit((unsigned char)p[i])) return 0;
    return 1;
}
/* looks_like_plate("MH12AB1234") → 1, looks_like_plate("MH1AB1234") → 0 */

Practice (0/3 done)

Task 1 · Question

What does printf("%c", 'a' - 32); print?

Task 2 · Code · Javascript

The same Caesar cipher algorithm in JavaScript, which runs here: write caesar(text, shift) that shifts A–Z and a–z by shift places (wrapping around, negative shifts allowed) and leaves other characters unchanged. Use character codes (charCodeAt and String.fromCharCode), like C char arithmetic.

Task 3 · Practice

Write C functions: int count_vowels(const char *s), void to_title_case(char *s) (first letter of each word upper case, the rest lower case) and int is_valid_ifsc(const char *s) (11 characters: 4 letters, then '0', then 6 letters or digits). Test each with at least three strings.

Quick quiz

Question 1

1.What is '9' - '0'?

Question 2

2.Why cast to unsigned char before isdigit(c)?

Question 3

3.toupper('5') returns…

Question 4

4.Can plain char functions process Hindi text correctly?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is '9' - '0'?

Answer: 9

Digits are consecutive in ASCII.

Why cast to unsigned char before isdigit(c)?

Answer: Negative char values (on signed-char platforms) are undefined behaviour for ctype functions

Bytes above 127 become negative when char is signed.

toupper('5') returns…

Answer: '5'

Non-letters are returned unchanged.

Can plain char functions process Hindi text correctly?

Answer: No: UTF-8 Devanagari uses several bytes per character

Use a Unicode-aware library for such text.

char a[] = "x", b[] = "x"; What does a == b compare?Related

Answer: The addresses of the two arrays

Use strcmp for text.

From Strings are char arrays

What type do you get from s[0] when s is a string?Related

Answer: byte (uint8)

Indexing a string gives a single byte.

From Strings, bytes and runes

How many bytes does a Rust char use?Related

Answer: 4

A char is a Unicode scalar value stored in 4 bytes.

From Scalar types: numbers, bool and char

Base64-encoded text is…Related

Answer: Just encoded: anyone can decode it

Encoding changes format only; there is no key.

From Symmetric and asymmetric encryption

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Strings in C” before its exam: Buffer overflows and safe string handling.
  2. Module 4 exam: Strings in C · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Pointers. 60 topics left in the course before the final exam.

← Previous: Reading strings safely