
The easiest way to mess with characters in C is to employ the ctype functions. I name this category of library functions after the ctype.h header file that defines them. Some are true functions, some are macros. Each function can be replicated with standard C code, though it’s quicker to use an established function than to code your own. But that’s not the point of learning to program!
The following code demonstrates the toupper() and tolower() ctype functions. As with all ctype functions, these functions work with with single characters. These two ctype functions are unique, however, in that they return values other than TRUE and FALSE: The toupper() function returns the uppercase character of the argument passed; tolower() returns the lowercase character. These functions have no effect on non-alphabetic characters, returning the character passed as-is.
2026_05_09-Lesson-a.c
#include <stdio.h>
#include <ctype.h>
int main()
{
char string[] = "Nothing beats knowing the ASCII codes!\n";
char *s;
/* uppercase */
s = string;
while( *s != '\0' )
{
putchar( toupper(*s) );
s++;
}
/* lowercase */
s = string;
while( *s != '\0' )
{
putchar( tolower(*s) );
s++;
}
return 0;
}
The two while loops output the characters in array string[]. The first loop sends the character through the toupper() function; the second loop sends the character through the tolower() function. Here’s the output:
NOTHING BEATS KNOWING THE ASCII CODES! nothing beats knowing the ascii codes!
If you’re going to emulate these functions, it helps to know the ASCII code values for letters A through Z. The ASCII table is organized in such a fashion that uppercase and lowercase letters differ by a single bit, as shown in the following table:
| A | 65 | 0x41 | 0100 0001 | a | 97 | 0x61 | 0110 0001 |
| B | 66 | 0x42 | 0100 0010 | b | 98 | 0x62 | 0110 0010 |
| C | 67 | 0x43 | 0100 0011 | c | 99 | 0x63 | 0110 0011 |
| D | 68 | 0x44 | 0100 0100 | d | 100 | 0x64 | 0110 0100 |
| E | 69 | 0x45 | 0100 0101 | e | 101 | 0x65 | 0110 0101 |
| F | 70 | 0x46 | 0100 0110 | f | 102 | 0x66 | 0110 0110 |
| G | 71 | 0x47 | 0100 0111 | g | 103 | 0x67 | 0110 0111 |
| H | 72 | 0x48 | 0100 1000 | h | 104 | 0x68 | 0110 1000 |
| I | 73 | 0x49 | 0100 1001 | i | 105 | 0x69 | 0110 1001 |
| J | 74 | 0x4A | 0100 1010 | j | 106 | 0x6A | 0110 1010 |
| K | 75 | 0x4B | 0100 1011 | k | 107 | 0x6B | 0110 1011 |
| L | 76 | 0x4C | 0100 1100 | l | 108 | 0x6C | 0110 1100 |
| M | 77 | 0x4D | 0100 1101 | m | 109 | 0x6D | 0110 1101 |
| N | 78 | 0x4E | 0100 1110 | n | 110 | 0x6E | 0110 1110 |
| O | 79 | 0x4F | 0100 1111 | o | 111 | 0x6F | 0110 1111 |
| P | 80 | 0x50 | 0101 0000 | p | 112 | 0x70 | 0111 0000 |
| Q | 81 | 0x51 | 0101 0001 | q | 113 | 0x71 | 0111 0001 |
| R | 82 | 0x52 | 0101 0010 | r | 114 | 0x72 | 0111 0010 |
| S | 83 | 0x53 | 0101 0011 | s | 115 | 0x73 | 0111 0011 |
| T | 84 | 0x54 | 0101 0100 | t | 116 | 0x74 | 0111 0100 |
| U | 85 | 0x55 | 0101 0101 | u | 117 | 0x75 | 0111 0101 |
| V | 86 | 0x56 | 0101 0110 | v | 118 | 0x76 | 0111 0110 |
| W | 87 | 0x57 | 0101 0111 | w | 119 | 0x77 | 0111 0111 |
| X | 88 | 0x58 | 0101 1000 | x | 120 | 0x78 | 0111 1000 |
| Y | 89 | 0x59 | 0101 1001 | y | 121 | 0x79 | 0111 1001 |
| Z | 90 | 0x5A | 0101 1010 | z | 122 | 0x7A | 0111 1010 |
Several clever and fun approaches are available for changing uppercase to lowercase, one of which involves setting or resetting the fifth bit. But for my emulation functions, I use a comparison:
2026_05_09-Lesson-b.c
#include <stdio.h> /* convert to uppercase */ int toupper(int c) { if( c>='a' && c<='z' ) c -= 32; return(c); } /* convert to lowercase */ int tolower(int c) { if( c>='A' && c<='Z' ) c += 32; return(c); } int main() { char string[] = "Nothing beats knowing the ASCII codes!\n"; char *s; /* uppercase */ s = string; while( *s != '\0' ) { putchar( toupper(*s) ); s++; } /* lowercase */ s = string; while( *s != '\0' ) { putchar( tolower(*s) ); s++; } return 0; }
This update to the code removes including the ctype.h header file. Instead, I wrote my own toupper() and tolower() functions, following the ctype format. Each of my emulations works the same: An if test checks for a letter of the alphabet, upper- or lowercase. If true, the value 32 is subtracted from the character’s value to convert it to uppercase; the value 32 is added to the character’s value to convert it to lowercase. Otherwise, the same character is returned.
The program’s output is the same:
NOTHING BEATS KNOWING THE ASCII CODES! nothing beats knowing the ascii codes!
In the toupper() function, you could replace the expression c -= 32 with c &= 0xDF By resetting the fifth bit, you convert a lowercase letter to uppercase. Likewise, in the tolower() function you can replace c += 32 with c |=0x20 These modifications work, relying upon bit manipulation to set or reset the bits to make the character conversion. I don’t know which approach is used internally by the C library, though either way works.
Next week I explore the isupper() and islower() functions.