C Part I · Module 2 · Values, Variables, Types and Representation
Why C Has Different Integer Types
A reasonable question, once you have seen int a few times: why does C not simply have one kind of whole number and use it everywhere? The answer is a design decision made under real constraints, and it comes in two parts that beginners usually meet as one confusing lump. C offers a family of integer types because different jobs genuinely need different amounts of room, and it deliberately refuses to fix their exact sizes because pinning them down would have made C unusable on half the machines it was meant to run on. What the standard promises instead is a floor — a minimum range each type must be able to hold — and knowing the difference between that floor and what your own machine happens to provide is the difference between portable C and code that works until someone builds it somewhere else.
Foundation12 min readCPart IFundamentalsPortability
Part I · Module 2 · Chapter 2.5 · Why C Has Different Integer Types
1. Why Not Just One Kind of Number
Every program in this curriculum so far has used int and nothing else. So the question is fair: why does C bother with more than one?
Because storage is a real cost and range is a real requirement, and the two pull in opposite directions.
A value that will only ever be between 0 and 200 does not need the same room as one that might reach two billion. Most of the time that difference is irrelevant — a few extra bytes on your laptop costs nothing you will notice. But C was built for the work Chapter 1.2 described, and there the difference is not always irrelevant:
- A controller with a few kilobytes of memory, holding thousands of values.
- A data format where a field is defined as occupying a particular amount of room, and your code has to agree with it.
- A machine whose natural working size is 16 bits, where insisting on 64 makes every operation slower.
So C gives you a family and lets you choose. The cost of that choice is that you have to make it.
2. The Family
From smallest to largest, C's signed integer types are:
char smallest
short
int
long
long long largestchar is in that list because it genuinely is an integer type, which is a surprise worth its own chapter — 2.8. For now, read the list as: there are several sizes, arranged from small to large.
int sits in the middle, and it has a special status that is easy to miss. It is meant to be the natural size for the machine — the size arithmetic is most comfortable in. That is why int is the default choice and why the rest of this module keeps using it: unless you have a reason to pick something else, int is the reason-free option.
3. C Does Not Tell You How Big They Are
Here is the part that catches everyone.
You might expect the standard to say int is four bytes. It does not. It says almost nothing about sizes, and instead promises minimum ranges:
| Type | Guaranteed to hold at least |
|---|---|
short | −32767 to 32767 |
int | −32767 to 32767 |
long | −2147483647 to 2147483647 |
long long | −9223372036854775807 to 9223372036854775807 |
char is deliberately absent from that table. Its guaranteed range depends on something not yet settled — whether plain char holds negative values at all, which is not fixed by the standard and is Chapter 2.8's subject.
Two things in the table are worth a second look.
short and int have the same guarantee. On a machine where 16 bits is natural, they may genuinely be the same size. On your laptop int is almost certainly larger. Both are conforming.
Each type must be at least as large as the one before it, but they may be equal. The family is a non-decreasing sequence, not a strictly increasing one.
In Chapter 1.3's vocabulary, the exact size of each type is implementation-defined: your implementation chooses and must document its choice. What the standard fixes is the floor.
4. What Your Own Machine Provides
The standard tells you the floor. To find out what you actually have, ask. The header <limits.h> supplies names for the limits of each type on your implementation:
#include <limits.h>
#include <stdio.h>
int main(void)
{
printf("int from %d to %d\n", INT_MIN, INT_MAX);
printf("short from %d to %d\n", SHRT_MIN, SHRT_MAX);
printf("long from %ld to %ld\n", LONG_MIN, LONG_MAX);
return 0;
}On the machine this chapter was written on:
int from -2147483648 to 2147483647
short from -32768 to 32767
long from -9223372036854775808 to 9223372036854775807Those numbers are what this machine provides, not what C promises. Compare them against §3's table: every one is at or above the guaranteed floor, and int and long are both comfortably above it. Run the same program on a small embedded target and you may see smaller numbers, all still conforming.
INT_MAX and the rest are names the header gives you. They are not variables and you cannot assign to them; Chapter 11.6 explains the mechanism. Using them is the point here.
5. What Happens When a Value Does Not Fit
Choosing a type too small is not a theoretical risk. Try to put 40000 into a short:
short small = 40000;The compiler tells you:
warning: implicit conversion from 'int' to 'short' changes value from 40000 to -25536 [-Wconstant-conversion]Read that carefully, because it is doing you a large favour. The compiler has worked out that 40000 will not fit in a short on this machine, that the value stored will be something else entirely, and that the something else is negative — which for a quantity is almost certainly not what you meant.
It is a warning, not an error. The program still builds. Without the options from Chapter 1.5 you would be told nothing at all, and the first sign of trouble would be a negative count somewhere far from this line.
Note also that the warning names a specific replacement value. That is this compiler being able to compute it here because the value is written in the source. When the value only becomes too large while the program runs, no compiler can warn you — which is why choosing the type correctly matters more than relying on diagnostics.
6. Choosing a Type
For this module and most of Part I, the guidance is short:
Use int unless you have a reason not to. It is the natural size for the machine, it is what the rest of this curriculum uses, and "I might need a different one" is not a reason.
Reach for a larger type when the guaranteed floor is not enough. If a value might exceed roughly ±32767 and your code has to run anywhere, int is not sufficient by the standard's promise — long is. On your laptop int will cope; on a small target it may not.
Reach for a smaller type when room genuinely matters — many thousands of values, or a constrained device. Not for a handful of variables, where the saving is nothing and the risk in §5 is real.
Do not choose a type by guessing its size. If your code needs a value of an exact width — because a hardware register or a data format says so — then minimum ranges are not what you want at all. C has types for that, and Chapter 14.2 introduces them, at the point in this curriculum where you first genuinely need one.
7. What You Should Be Able to Say Now
- C offers a family of integer types because range and storage cost pull in opposite directions, and different jobs land differently between them.
- From smallest to largest:
char,short,int,long,long long. Each is at least as large as the one before, and two may be equal. intis meant to be the natural size for the machine, which is why it is the default choice.- The standard fixes minimum ranges, not sizes. An
intis guaranteed to hold at least −32767 to 32767 and nothing more. - The exact size of each type is implementation-defined — chosen and documented by your implementation.
- The vagueness is deliberate: it is what lets C run on machines with very different natural word sizes.
<limits.h>tells you what your own machine actually provides. Those numbers are facts about your machine, not about C.- Storing a value too large for a type changes it, and the compiler warns only when it can see the problem at compile time.
8. Exercise
Exercise 1 — read the guarantee. A program stores a value that may reach 100000. Is int sufficient according to the C standard? Is it sufficient on the machine in §4? Explain why those two answers can differ.
Exercise 2 — run it yourself. Compile and run the <limits.h> program from §4 on your own machine and compare every number against §3's table. Which types exceed their guaranteed floor, and by how much?
Exercise 3 — predict the diagnostic. For each, say whether the compiler can warn you at compile time, and why or why not:
(a) short count = 70000;
(b) A short object that has 1 added to it repeatedly while the program runs, eventually passing its maximum.
Exercise 4 — reject the claim. A colleague writes: "An int is 32 bits, so it holds up to about two billion." Give two separate reasons this is not a safe thing to write down, using §3.
Exercise 5 — choose. For each, name a type and give a one-line reason: the number of people in a room; the number of microseconds since a program started; a value a hardware document defines as occupying exactly 16 bits; a loop counter that will not exceed 100.
9. Summary
- C has a family of integer types because range and storage genuinely trade off, and C was built for machines where that trade-off is not free.
char,short,int,long,long long, each at least as large as the last, and possibly equal.intis the natural size for the machine and the right default.- The standard guarantees minimum ranges, not sizes.
intis promised only −32767 to 32767. - Exact sizes are implementation-defined — deliberately, so that C can be implemented well on machines that differ.
<limits.h>reports your machine. Those numbers describe one implementation and must not be written into your assumptions.- A value that does not fit is changed, not rejected. The compiler warns when it can see the value; often it cannot.
Every range in this chapter ran from a negative number to a positive one. That was a choice, and C lets you make a different one — a type that spends all its room on non-negative values instead. The next chapter is about that choice, and about the fact that the two options behave very differently at their edges in a way that produces one of the most persistent bug patterns in C.
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.
