# ctype.h **`ctype.h`** gives you the character classification and conversion functions you reach for when parsing input character by character. If you have ever written `if (c >= 'a' && c <= 'z')` to check for a lowercase letter, these functions are the right way to do it — they handle the full range of `unsigned char` values correctly and respect the current locale. ```c #include isalpha('a') // non-zero: it's a letter isdigit('7') // non-zero: decimal digit isalnum('Z') // non-zero: letter or digit isspace(' ') // non-zero: space, tab, newline, \r, \f, \v isupper('A') // non-zero: uppercase letter islower('b') // non-zero: lowercase letter ispunct('!') // non-zero: printable, not space, not alnum isprint('~') // non-zero: printable including space iscntrl('\n') // non-zero: control character toupper('a') // 'A' tolower('Z') // 'z' ``` There is one gotcha: these functions take an `int` that must be either an `unsigned char` value or `EOF`. If you pass a plain `char` and your platform has signed `char` (most do), characters above 127 come in as negative values and trigger undefined behaviour. Always cast: `isalpha((unsigned char)c)`. The functions are locale-dependent. `isalpha` returns non-zero for letters in the current locale, which may include `é`, `ü`, `ñ` and other characters outside ASCII. ## Practice ```c // compile: gcc -o ident ident.c // run: echo "hello_world" | ./ident echo "123bad" | ./ident // description: validate whether stdin contains a valid C identifier #include #include int main(void) { int c, pos = 0, valid = 1; while ((c = getchar()) != '\n' && c != EOF) { unsigned char uc = (unsigned char)c; if (pos == 0 && !isalpha(uc) && c != '_') valid = 0; if (pos > 0 && !isalnum(uc) && c != '_') valid = 0; pos++; } puts(valid && pos > 0 ? "valid identifier" : "not valid"); return 0; } ``` Try `hello_world` (valid), `123bad` (fails: starts with a digit), `_ok` (valid), and `café` — the last one will vary by locale, which is a useful reminder that `isalpha` sees locale-defined letters, not just ASCII. The `(unsigned char)` cast is the correct form in production code.