A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
The ctype.h header provides functions that classify and transform characters. isalpha, isdigit, isspace, toupper, tolower, and their siblings. They take an int argument (not char, for reasons we will cover) and return nonzero for true, zero for false. Using these functions instead of manual range checks like c >= 'a' && c <= 'z' is not just cleaner. It is locale-aware and it avoids undefined behavior when the character value is negative. Every C programmer who writes c >= '0' && c <= '9' is writing code that a reviewer will flag.
#include <stdio.h>#include <ctype.h>int main(void) {const char *test = "Hello, World! 123\n";for (const char *p = test; *p; p++) {unsigned char c = *p;printf("'%c' (0x%02X): ", isprint(c) ? c : '?', c);if (isalpha(c)) printf("alpha ");if (isdigit(c)) printf("digit ");if (isalnum(c)) printf("alnum ");if (isspace(c)) printf("space ");if (ispunct(c)) printf("punct ");if (isupper(c)) printf("upper ");if (islower(c)) printf("lower ");printf("\n");}return 0;}
The ctype functions take an int, not a char. The argument must be representable as unsigned char or equal to EOF. If you pass a plain char that happens to be negative (which happens for bytes above 127 on platforms where char is signed), the behavior is undefined. The fix: cast to unsigned char before passing. isalpha((unsigned char)c). This is not pedantry. It is a real bug on real systems processing UTF-8 or binary data.
#include <stdio.h>#include <ctype.h>int main(void) {char negative = (char)0xE9;printf("Unsafe: isalpha(c) = %d\n", isalpha(negative));printf("Safe: isalpha(uc) = %d\n", isalpha((unsigned char)negative));return 0;}
The undefined behavior sanitizer flags the unsafe call. The safe call with the unsigned char cast is well-defined. Both happen to return 0 on this platform, but the unsafe version could crash, return garbage, or behave differently on another system. Always cast.
#include <stdio.h>#include <ctype.h>#include <string.h>void to_upper_inplace(char *s) {for (; *s; s++) {*s = toupper((unsigned char)*s);}}int main(void) {char msg[] = "hello world 123";printf("Before: %s\n", msg);to_upper_inplace(msg);printf("After: %s\n", msg);return 0;}
toupper and tolower return the converted character if a conversion exists, or the original character unchanged if not. toupper('5') returns '5'. tolower('!') returns '!'. They are safe to call on any character. No pre-check needed. The unsigned char cast is still required.
The ctype library is small but essential. It replaces fragile ASCII arithmetic with portable, locale-aware functions. We cover each function individually in the following lessons, but the pattern is the same for all of them: cast to unsigned char, call the function, interpret nonzero as true. Next: memcpy, the raw memory copy function that underlies every buffer operation.
Finish the lesson once you have worked through the material. This awards ★ 20 XP.