What is C Character Set Explained-Free C Programming Course

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

C Character Set Explained

Lecture 1 of the Elements of C chapter. Part of our free embedded systems course: learn the letters, digits, symbols and delimiters every C program is built from.

91
graphic characters in the basic set
4
runnable examples
8
quiz questions

The C character set is the collection of letters, digits, symbols and spacing characters that the compiler is willing to read in your source file. Everything you will ever write in C, from a one-line printf to a Linux driver, is built from these characters and nothing else. If you are following this free embedded systems course to prepare for product-company interviews, this is the foundation: interviewers love to probe what the compiler does with a character, not just what a program prints.

By the end of this lecture you will be able to explain how the compiler turns raw characters into tokens, tell delimiters from operators, and write small programs that classify and manipulate characters using their numeric codes.

What You Will Learn

  • What the C character set contains and why the standard calls it the basic source character set.
  • How letters, digits and special characters are used in real C syntax.
  • The difference between the source character set and the execution character set.
  • What whitespace really does, and why int x is not the same as intx.
  • The job of each delimiter: colon, semicolon, parentheses, brackets, braces, hash and comma.
  • How to classify characters with ctype.h and convert between characters and numbers.
  • Typical beginner errors, a quiz, and interview questions asked on this topic.

Prerequisites

  • A working compiler. On Ubuntu or Debian run sudo apt install gcc; on Windows use WSL or MinGW.
  • Ability to save a .c file and compile it from a terminal. Chapter 1 of this free embedded C course covered setup.
  • No earlier C knowledge beyond printf is required.

Why the C Character Set Matters

A C source file is just text. Before the compiler can understand a single idea in it, it performs lexical analysis: it reads characters one by one and groups them into the smallest meaningful units, called tokens. A token can be a keyword like while, a name like total, a number like 42, a string, an operator like +, or a punctuator like ;. The character set decides which characters are allowed to take part in this process.

How characters become a program
Stage 1Source file: plain characters such as i n t, space, t o t a l, =, 5, ;
Stage 2Lexer groups characters into tokens: int, total, =, 5, ;
Stage 3Parser checks grammar and builds a syntax tree
Stage 4Code generator emits machine instructions

Because the very first stage is character-driven, a single wrong character, for example a curly quote pasted from a web page, stops the build immediately. Understanding the set saves hours of confusing compiler messages.

The Three Building Blocks of the C Character Set

The basic character set is divided into letters, digits and special characters. Whitespace is added on top of these as a fourth group that controls layout.

Group Members Count Typical use
Letters A to Z and a to z 52 Keywords, variable and function names
Digits 0 to 9 10 Numeric constants, parts of names
Graphic symbols Punctuation and operator characters 29 Operators, delimiters, quotes, escapes
Whitespace Space, horizontal tab, vertical tab, form feed, newline 5 Separating tokens, formatting

Adding 52 letters, 10 digits and 29 graphic symbols gives the 91 graphic characters of the basic set. Notice that the underscore is counted among the 29 symbols, yet the language treats it like a letter when you build names.

Alphabets: Case Matters

C distinguishes uppercase from lowercase everywhere. Speed, speed and SPEED are three unrelated names, and While is not the keyword while. Behind the scenes every letter is stored as a small integer. In ASCII, 'A' is 65 and 'a' is 97. Uppercase and lowercase letters of the same pair differ by exactly 32, which is a single bit (bit 5). You will use this fact in the second example.

Digits: Characters Versus Numbers

The character '7' and the number 7 are different things. The character has a code (55 in ASCII); the number is a value. The C standard guarantees that the digit characters are consecutive, so '7' - '0' always gives 7 on any platform. That guarantee does not exist for letters on every encoding, so digit arithmetic is portable while letter arithmetic is only safe on ASCII-compatible systems.

Special Characters and What They Do in C

Instead of memorising a flat list of names, group the symbols by the role they play in the language.

Role Characters Where you meet them
Arithmetic operators + - * / % total = a + b;
Relational and logical < > = ! & | if (x >= 10 && y != 0)
Bitwise & | ^ ~ mask = flags & 0x0F;
Grouping delimiters ( ) [ ] { } Calls, arrays, blocks
Separators , ; : . Argument lists, statement ends, labels, struct members
Conditional operator ? : max = (a > b) ? a : b;
Quoting ' " 'A' and "hello"
Escape introducer "linen" (next lecture)
Preprocessor # #include, #define
Name builder _ sensor_value

Symbols that are not in the basic set

Older textbooks list @, $ and the back-quote among the special characters. They are not part of the basic source character set and have no meaning in C syntax. You may still place them inside string literals and comments. GCC also accepts $ inside identifiers as an extension, but you should avoid it in portable code.

Whitespace: The Invisible Separator

Space, horizontal tab, vertical tab, form feed and newline are collectively called whitespace. Their main job is to separate tokens. The compiler needs int total with a gap, because without it the lexer would see one long name, inttotal. Beyond that gap, extra whitespace is ignored, which is why you can indent code freely.

The lexer uses a rule called maximal munch: it always builds the longest token it can. That is why a+++b is read as a++ + b and not a + ++b. Whitespace is your tool for steering the lexer when that matters.

Source Character Set Versus Execution Character Set

There are two character sets in play, and mixing them up is a classic interview trap.

Aspect Source character set Execution character set
Where it applies The text of your .c file Characters inside the running program
Minimum content The 91 graphic characters plus whitespace The same, plus control characters like newline and backspace
Example Writing the two characters and n One newline character with code 10
Chosen by Compiler, editor encoding Compiler and target platform

On modern Linux both are usually ASCII-compatible UTF-8, so the difference feels invisible. On a microcontroller toolchain or a mainframe, it is real. This is also why the next lecture introduces escape sequences: they are the bridge from characters you can type to characters the program can use.

C Delimiters (Punctuators) in Detail

Delimiters give a program its structure. The standard calls them punctuators, but most books, and many interviewers, use the word delimiter.

Delimiter Name Purpose Example
: Colon Ends a label, separates the parts of ? :, follows case and default again:
; Semicolon Terminates a statement or declaration total = 0;
( ) Parentheses Group expressions, enclose call arguments and conditions if (x > 0)
[ ] Square brackets Declare and index arrays marks[2]
{ } Curly braces Form a block, enclose initialisers and function bodies { 70, 85 }
# Hash Begins a preprocessor directive #define N 3
, Comma Separates declarators and arguments; also an operator int a, b;
Anatomy of: int marks[3] = { 70, 85, 90 };
intkeyword: the element type
marksidentifier: the name you chose
[ 3 ]square brackets delimit the array size
=operator: initialise
{ 70, 85, 90 }braces group the list; commas separate the values
;semicolon ends the declaration

Hands-On Examples: Practise the C Character Set

Every example below was compiled with gcc -Wall -Wextra -std=c17 and the output shown is real. Type them in yourself rather than pasting; the muscle memory is part of the learning.

Example 1: Classify Every Character

Goal: walk through a string and count letters, digits, spaces and everything else. This shows the groups of the character set in action and introduces ctype.h.

#include <stdio.h>
#include <ctype.h>

int main(void)
{
    const char *text = "Sensor#7 reads 42.5C @ 09:30!";
    int letters = 0, digits = 0, spaces = 0, others = 0;

    for (const char *p = text; *p != ''; p++) {
        unsigned char ch = (unsigned char)*p;
        if (isalpha(ch))
            letters++;
        else if (isdigit(ch))
            digits++;
        else if (isspace(ch))
            spaces++;
        else
            others++;
    }

    printf("Text    : %sn", text);
    printf("Letters : %dn", letters);
    printf("Digits  : %dn", digits);
    printf("Spaces  : %dn", spaces);
    printf("Others  : %dn", others);
    return 0;
}

Build and run:

gcc -Wall -Wextra -std=c17 classify.c -o classify
./classify

Expected output:

Text    : Sensor#7 reads 42.5C @ 09:30!
Letters : 12
Digits  : 8
Spaces  : 4
Others  : 5
  • The cast to unsigned char before calling isalpha is deliberate. The ctype functions accept values in the unsigned range, and passing a negative char is undefined behaviour.
  • The string contains #, ., @, : and !, which makes the five “others”.
Try it yourself: change the string to your own name and a phone number

Replace the text with something like "Asha 98480-12345". You should see 4 letters, 10 digits, 1 space and 1 other (the hyphen). If your counts differ, check whether you accidentally typed a non-ASCII dash.

Example 2: Characters Are Small Numbers

Goal: prove that a character is an integer in disguise, and use that fact to change case and convert a digit to a value.

#include <stdio.h>

int main(void)
{
    char upper = 'G';
    char lower = upper ^ 0x20;   /* flip bit 5 */
    char digit = '7';

    printf("'%c' has code %dn", upper, upper);
    printf("'%c' has code %dn", lower, lower);
    printf("Gap between 'a' and 'A' : %dn", 'a' - 'A');
    printf("Digit '%c' is the number %dn", digit, digit - '0');

    for (char c = 'A'; c <= 'E'; c++)
        printf("%c=%d ", c, c);
    printf("n");
    return 0;
}
'G' has code 71
'g' has code 103
Gap between 'a' and 'A' : 32
Digit '7' is the number 7
A=65 B=66 C=67 D=68 E=69 
  • upper ^ 0x20 flips bit 5, turning 'G' (71) into 'g' (103). The same trick converts back.
  • digit - '0' is the portable way to turn a digit character into its value.
  • The loop for (char c = 'A'; c <= 'E'; c++) works because characters support arithmetic and comparison like any integer.
Try it yourself: convert ‘z’ to uppercase without using toupper

Use 'z' - 32 or 'z' ^ 0x20; both give 90, which is 'Z'. Remember this only works for letters, because applying the XOR to a digit or symbol produces a different, unrelated character.

Example 3: Special Characters as Operators

Goal: see the arithmetic, bitwise, logical and pointer roles of special characters in one run.

#include <stdio.h>

int main(void)
{
    int a = 12, b = 5;
    int *p = &a;

    printf("a + b = %d, a - b = %dn", a + b, a - b);
    printf("a * b = %d, a / b = %d, a %% b = %dn", a * b, a / b, a % b);
    printf("a & b = %d, a | b = %d, a ^ b = %d, ~a = %dn", a & b, a | b, a ^ b, ~a);
    printf("!a = %d, a > b = %d, a == b = %dn", !a, a > b, a == b);
    printf("value through *p = %dn", *p);
    printf("100%% donen");
    return 0;
}
a + b = 17, a - b = 7
a * b = 60, a / b = 2, a % b = 2
a & b = 4, a | b = 13, a ^ b = 9, ~a = -13
!a = 0, a > b = 1, a == b = 0
value through *p = 12
100% done
  • The percent sign has two jobs. In a % b it computes the remainder. In a printf format string you must write %% to print a literal percent.
  • With a = 12 (binary 1100) and b = 5 (binary 0101), AND gives 0100 = 4, OR gives 1101 = 13, XOR gives 1001 = 9.
  • The asterisk is multiplication in a * b but dereference in *p. The ampersand is bitwise AND between two values but address-of in &a. Context decides, which is why the lexer alone cannot understand a program.
Try it yourself: predict ~b before running

For b = 5 the answer is -6. The tilde flips every bit, and on a two’s complement machine flipping all bits of n gives -(n + 1).

Example 4: Delimiters at Work

Goal: use a colon label, square brackets, braces, commas, a semicolon and a hash directive in one small program.

#include <stdio.h>
#define LIMIT 3

int main(void)
{
    int marks[LIMIT] = { 70, 85, 90 }, total = 0, i = 0;

again:
    total += marks[i];
    i++;
    if (i < LIMIT)
        goto again;

    {
        int average = total / LIMIT;
        printf("total=%d average=%dn", total, average);
    }
    return 0;
}
total=245 average=81
  • #define LIMIT 3 starts with a hash, so the preprocessor handles it before compilation.
  • again: is a label: a name followed by a colon. goto again; jumps back to it, forming a loop.
  • The bare pair of braces creates an inner block whose variable average exists only inside it.
  • 245 divided by 3 is 81 in integer arithmetic; the fractional part is discarded.
Try it yourself: remove one semicolon and read the error

Delete the semicolon after total += marks[i]. GCC reports an “expected ‘;'” style error that names the token which followed. The compiler only realises something is wrong when that next token arrives, so the real fault is at the end of the previous line. Learning to look one line up is a real-world debugging skill.

Common Mistakes with the C Character Set

Mistake Symptom Fix
Pasting curly quotes from a document Error such as stray ‘342’ in program Retype the quotes as plain " and '
Using 'abc' for a string Warning about a multi-character constant Single quotes are for one character, double quotes for strings
Writing = instead of == in a condition Condition always true or false Compile with -Wall, which flags it
Forgetting %% in printf Garbage or missing output Double the percent sign
Passing a plain char to isalpha Undefined behaviour for negative values Cast to unsigned char
Using @ or $ in code Stray character error Keep them inside strings and comments

Best Practices, Performance and Security Notes

  • Save source files as UTF-8 without a byte-order mark and keep code itself pure ASCII; put non-ASCII text only in comments or in string data you intend to handle.
  • Prefer isalpha, isdigit and isspace over hard-coded numeric ranges. They are clearer and respect the platform encoding.
  • Always compile with -Wall -Wextra. Most character-level mistakes are caught as warnings.
  • Performance: character classification is cheap, usually a table lookup, so do not hand-roll range checks for speed.
  • Security: when you validate input, decide explicitly which characters are allowed (an allow-list) instead of trying to block bad ones. Unexpected control characters in input are a common source of injection and log-forging bugs in embedded Linux services.

Summary and Key Takeaways

  • The C character set is 52 letters, 10 digits, 29 graphic symbols and the whitespace characters.
  • The lexer groups characters into tokens using maximal munch; whitespace separates tokens.
  • Characters are small integers; '7' - '0' is 7 and a case flip is a single XOR with 32 in ASCII.
  • @, $ and the back-quote are not in the basic source set.
  • Delimiters: colon for labels, semicolon to end statements, parentheses for grouping, brackets for arrays, braces for blocks, hash for the preprocessor, comma as a separator.
  • Source and execution character sets are different ideas; escape sequences connect them.

Quiz: Test Your Understanding

Pick an answer first, then open the box to check.

Question 1

How many graphic characters are in the basic C source character set?

A) 52   B) 62   C) 91   D) 128

Show answer

C. 52 letters + 10 digits + 29 symbols = 91.

Question 2

Which of these is NOT part of the basic source character set?

A) #   B) ~   C) @   D) _

Show answer

C. The at sign has no role in C syntax and is outside the basic set.

Question 3

What does '9' - '0' evaluate to?

Show answer

9, an int. Digit characters are guaranteed to be consecutive.

Question 4

Which delimiter ends a statement?

A) colon   B) comma   C) semicolon   D) hash

Show answer

C, the semicolon.

Question 5

How is a+++b tokenised?

Show answer

As a ++ + b. Maximal munch grabs ++ first, so it means (a++) + b.

Question 6

What is the numeric difference between 'a' and 'A' in ASCII?

Show answer

32, which is bit 5.

Question 7

Which statement prints a literal percent sign?

A) printf("%");   B) printf("%%");   C) printf("%");

Show answer

B. The format parser treats a single percent as the start of a conversion.

Question 8

True or false: extra spaces between tokens change the meaning of a C program.

Show answer

False, with exceptions: spaces inside string literals are data, and a space may be needed to separate two tokens that would otherwise merge, as in int x.

Interview Questions on the C Character Set

Q1. What is the difference between a character constant and a string literal?

Show answer

'A' is a single character with type int in C and the value 65. "A" is an array of two chars, the letter and a terminating null. Confusing them is a classic bug, because passing 'A' where a char * is expected compiles with a warning and crashes at run time.

Q2. What is maximal munch?

Show answer

The lexer rule that, at each position, builds the longest sequence of characters that forms a valid token. It explains why x+++++y fails to compile: it tokenises as x ++ ++ + y, and the second ++ is applied to a non-lvalue.

Q3. Why cast to unsigned char before calling isalpha?

Show answer

The ctype functions take an int that must be representable as unsigned char or equal EOF. A plain char holding a byte above 127 may be negative on signed-char platforms, which makes the call undefined.

Q4. Is the comma a delimiter or an operator?

Show answer

Both. In declarations and function calls it separates items. In an expression such as x = (a++, b) it is the comma operator that evaluates the left side, discards it, and yields the right side.

Q5. Write a portable function that tells if a character is a hexadecimal digit without using ctype.

Show answer

Check three ranges: c >= '0' && c <= '9', c >= 'a' && c <= 'f', c >= 'A' && c <= 'F'. Digit ranges are guaranteed contiguous; letter ranges are contiguous in ASCII, which is why production code usually prefers isxdigit.

Q6. What is the difference between the source and execution character sets?

Show answer

The source set is what the compiler reads in your file; the execution set is what exists when the program runs. A backslash followed by n in source becomes a single newline in the execution set. The two may use different encodings when cross compiling.

Frequently Asked Questions

What is the C character set in simple words?

It is the list of characters you are allowed to type in a C program: letters, digits, a fixed group of symbols and whitespace.

Does C support Unicode characters in source code?

Modern compilers read UTF-8 files and allow non-ASCII text in strings and comments. Using non-ASCII letters in identifiers is allowed by the standard but poorly portable, so avoid it in embedded work.

Is the underscore a letter in C?

The standard lists it as a symbol, but for building identifiers it behaves like a letter, so it can start a name.

Why does my program fail with a stray character error?

Most often a smart quote, long dash or invisible non-breaking space was pasted from a document. Retype the line.

Are delimiters and operators the same thing?

No. Operators compute values; delimiters give structure. Some symbols, such as the comma and the colon, play both roles depending on context.

Is this free embedded systems course enough for interview preparation?

It is designed to be: each lecture pairs theory with runnable examples, a quiz and interview questions. Work through the examples by hand for the best result.

Conclusion

You have now seen the C character set from the compiler’s point of view: a stream of characters that is sliced into tokens, with whitespace as separators and delimiters as the structural glue. The programs you ran proved the key ideas: characters are numbers, the same symbol can have different jobs depending on context, and tiny character mistakes produce confusing errors. In the next lecture of this free embedded C programming course we move to escape sequences and trigraphs, which let a program use characters that cannot simply be typed.

Keep going with the free course

Finish the quiz, rerun each example with your own input, and continue with the next lecture on escape sequences.

 

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *