C Part I · Module 2 · Values, Variables, Types and Representation
char and Character Values
Chapter 2.5 listed char among C's integer types and immediately admitted that this is surprising, then left it for later. This is later. The surprise is worth sitting with, because char is not a text type that C happens to store as a number — it is an integer type, full stop, with a convention on top of it that says certain numbers correspond to certain characters. Seeing that clearly explains several things that are otherwise arbitrary: why you can do arithmetic on letters, why printing the same object two ways gives A and 65, and why char is the one basic type whose ability to hold negative values C deliberately refuses to specify.
Foundation11 min readCPart IFundamentalsRepresentation
Part I · Module 2 · Chapter 2.8 · char and Character Values
1. One Object, Two Readings
Start with the demonstration, because it does most of the work:
#include <stdio.h>
int main(void)
{
char letter = 'A';
printf("as a character: %c\n", letter);
printf("as a number: %d\n", letter);
return 0;
}Output:
as a character: A
as a number: 65One object. One value. Two printed results, and no conversion happened between them. The object held 65 the entire time; %c asked for it to be shown as the character that 65 corresponds to, and %d asked for it to be shown as a number.
This is Chapter 2.4's distinction again, in its most concrete form. The value is 65. Whether you see A or 65 is a question about how you chose to display it, not about what is stored.
2. 'A' Is a Number You Wrote as a Letter
The other half of the demonstration is the declaration. 'A' — single quotes, one character — is a character literal, and it is an integer value. Writing 'A' and writing 65 put the same thing in the object on a system whose character set corresponds that way.
Single quotes are a convenience, not a different kind of value. They let you write the number for a character without looking it up, which matters because nobody remembers that 'q' is 113.
Two notes on that, both worth having.
Single quotes and double quotes are not interchangeable. 'A' is one character value. "A" is a string literal, which is a different thing entirely and belongs to Module 8. Using one where the other is meant is a common early error and the compiler will tell you.
The numeric values are not fixed by C. Which number corresponds to 'A' is decided by the character set your implementation uses. Every system you are realistically going to meet uses a set in which 'A' is 65, 'B' is 66 and so on — but that is a fact about those systems, and C does not guarantee it. C does guarantee that the decimal digits '0' through '9' are consecutive and in order, which is occasionally useful and is the only ordering promise of its kind.
3. Arithmetic on Characters Is Just Arithmetic
Because a char holds a number, you can do arithmetic on it, and this stops being strange once §1 has landed:
#include <stdio.h>
int main(void)
{
char letter = 'A';
char next = letter + 1;
printf("%c then %c\n", letter, next);
return 0;
}Output:
A then BNothing special happened. 65 plus 1 is 66, and 66 displayed with %c is B. There is no character-successor operation in C; there is addition, applied to a number that you are choosing to think of as a letter.
This is also why the guarantee about '0' through '9' being consecutive is useful: subtracting '0' from a digit character gives its numeric worth, and that works because the standard promises the ordering.
4. The Part C Refuses to Decide
Now the technically important part, and the reason Chapter 2.5 left char out of its range table.
C has three distinct character types, not two:
signed char definitely holds negative values
unsigned char definitely does not
char ...decided by your implementationPlain char is a separate type from both of the others, and whether it behaves as signed or unsigned is implementation-defined — in Chapter 1.3's sense, your implementation chooses and documents it, and different implementations genuinely choose differently.
That is unusual. For every other integer type, plain means signed: int is signed int. For char, plain means neither, and the answer varies.
The practical consequence is narrow but real:
Do not rely on a plain
charbeing able to hold a negative value, and do not rely on it not being able to.
If you need a small integer type and the sign matters, say which you mean: write signed char or unsigned char. Use plain char for characters, which is what it is for, and where the question rarely arises.
5. char as the Byte Type
One more thing char is used for, which follows from §4 and explains something you will see often.
Chapter 2.2 established that memory is a run of bytes. char is the type whose size is exactly one byte — always, on every implementation, by definition rather than by convention. Chapter 2.12 makes that precise.
That makes the character types the natural way to talk about raw bytes, whether or not the bytes are text. When you see unsigned char in code that has nothing to do with letters, that is what it is doing: naming a byte, with the signedness question settled explicitly.
6. What You Should Be Able to Say Now
charis an integer type. It holds a number, and the text interpretation is a convention layered on top.'A'is a character literal — an integer value written in a way that saves you looking it up.%cdisplays a value as a character;%ddisplays it as a number. The object is unchanged either way.- Single quotes and double quotes mean different things:
'A'is a character value,"A"is a string literal and belongs to Module 8. - Which number corresponds to
'A'depends on the implementation's character set. C promises only that'0'through'9'are consecutive. - Arithmetic on a
charis ordinary arithmetic on the number it holds. - There are three character types. Whether plain
charis signed is implementation-defined. - If the sign matters, write
signed charorunsigned char. Use plaincharfor characters. - A
charis exactly one byte, which makes the character types the natural way to name raw bytes.
7. Exercise
Exercise 1 — predict. What does this print, and why does the second line not print a letter?
char c = 'Z';
printf("%c\n", c);
printf("%d\n", c);Exercise 2 — compute with letters. On a system where 'a' is 97, what does 'a' + 2 give, and what appears if you print it with %c? Answer both parts.
Exercise 3 — use the one guarantee. C promises '0' through '9' are consecutive. Given a char holding a digit character, write the expression that produces its numeric worth, and explain why the guarantee is what makes it valid.
Exercise 4 — spot the type error. One of these is wrong. Which, and what would the compiler object to?
char a = 'x';
char b = "x";Exercise 5 — reason about signedness. A program reads bytes from a file into a plain char and treats values as quantities from 0 to 255. Describe what happens on an implementation where plain char is signed, and give the one-word fix.
Exercise 6 — classify. Using Chapter 1.3's four categories, classify each: the numeric value of 'A'; whether plain char is signed; whether '0' through '9' are consecutive; the size of a char in bytes.
8. Summary
charis an integer type, not a text type. The letter interpretation is a convention applied on display.'A'is an integer value. Single quotes write a character's number without you looking it up.%cand%dshow the same object two ways, with no conversion in between.- The numbers are the implementation's character set, not C's. Only
'0'through'9'being consecutive is guaranteed. - Arithmetic on characters is ordinary arithmetic, which is why
'A' + 1gives'B'on an ASCII-compatible system. - Three character types, and plain
charis its own. Its signedness is implementation-defined — the one basic type where C declines to decide. - Say what you mean when the sign matters:
signed charorunsigned char. - A
charis one byte, which makes the character types how C talks about raw bytes.
Every type in this module so far has held whole numbers, exactly, with no loss. The next chapter introduces the first type that cannot do that — one that can represent an enormous range of values and yet cannot represent 0.1 precisely, which produces arithmetic that looks broken and is not.
Standards & specifications
- Governing standard
- ISO/IEC 9899 (the C language standard)(opens ISO/IEC (JTC 1/SC 22/WG 14) in a new tab)
Defines the C language itself — the type system, expression semantics, object model, storage duration and the behaviour categories a conforming implementation must honour. It is a FAMILY of editions (C89/C90, C99, C11, C17, C23) and this curriculum names the edition wherever a claim depends on one. It does not define any foreign-function interface to a hardware description language, and it does not define platform behaviour: what a memory-mapped access does on real silicon is the implementation's and the device's contract, not this standard's. Working drafts published by WG14 are freely available and are what most engineers read day to day; a draft is not the published standard.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the C Programming curriculum.
