C++ Part I · Module 3 · Types, Variables, and Initialization
Fundamental Types and Their Sizes
Chapter 3.1 said the type decides how much storage an object takes, and then did not say how much. The answer is more interesting than a table of numbers, because for most types the C++ standard deliberately refuses to fix one. It specifies minimum ranges and a required ordering, and leaves the exact sizes to the implementation. That is not an oversight — it is what lets the same language target a desktop processor, a server, and a microcontroller without pretending they are the same machine. The practical consequence is that there are two completely different kinds of statement you can make about a type's size, and an engineer who merges them writes code that works on one platform and silently truncates on another. This chapter keeps them apart: first what C++ promises everywhere, then what one real machine reported when asked, and then the small number of habits that follow.
Foundation14 min readC++Part IFundamentalsTypes
Part I · Module 3 · Chapter 3.2 · Fundamental Types and Their Sizes
1. The Built-in Types
C++ gives you a set of types that are part of the language itself, called the fundamental types. Everything else — every container, every class you write — is built from them.
They fall into four groups:
| Group | Types |
|---|---|
| Integer | short, int, long, long long, each with a signed and an unsigned form |
| Character | char, signed char, unsigned char, and the wider character types |
| Boolean | bool |
| Floating-point | float, double, long double |
There is also void, which is the type meaning "no value at all" — you have already seen it nowhere, because every type you have used so far holds something.
This chapter is about the sizes of these types. Chapters 3.3, 3.5 and 3.6 take the integer, floating-point and boolean/character groups properly.
2. Why C++ Does Not Fix the Sizes
It would be simpler if the standard said "an int is 32 bits" and stopped. It does not, and the reason is worth understanding because it shapes how you should write code.
C++ runs on an enormous range of hardware: desktop and server processors, embedded controllers, digital signal processors, and the machines inside the tools and test equipment this field uses. Those machines have genuinely different natural word sizes. Forcing every implementation to pretend otherwise would mean either slow code on some machines or no C++ on others.
So the standard specifies the two things that let you write portable code without fixing the sizes:
- A minimum range each type must be able to hold.
- An ordering that no implementation may violate.
Everything else is left to the implementation, which must document what it chose. Chapter 1.3 §5 gave this behaviour its proper name: it is implementation-defined.
3. What the Standard Actually Guarantees
These statements are true of every conforming C++ implementation, everywhere:
sizeof(char) is exactly 1. Not "usually 1" — by definition. The char type is the unit sizeof counts in.
Sizes do not decrease along this chain:
sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long) ≤ sizeof(long long)Note ≤, not <. It is entirely conforming for long and long long to be the same size, and on the machine used for this chapter they are.
Each integer type can represent at least a guaranteed minimum range. The standard expresses this through minimum sizes in bits: at least 8 for char, 16 for short and int, 32 for long, and 64 for long long.
A signed type and its unsigned counterpart are the same size. unsigned int takes exactly as much storage as int.
What is not guaranteed is the thing everybody assumes: the exact size of int, long, or anything except char.
4. Measuring Your Own Implementation
The standard tells you what is guaranteed. sizeof tells you what your platform chose. Both are worth knowing and they answer different questions.
#include <climits>
#include <iostream>
int main()
{
std::cout << "CHAR_BIT = " << CHAR_BIT << '\n';
std::cout << "sizeof(char) = " << sizeof(char) << '\n';
std::cout << "sizeof(bool) = " << sizeof(bool) << '\n';
std::cout << "sizeof(short) = " << sizeof(short) << '\n';
std::cout << "sizeof(int) = " << sizeof(int) << '\n';
std::cout << "sizeof(long) = " << sizeof(long) << '\n';
std::cout << "sizeof(long long) = " << sizeof(long long) << '\n';
std::cout << "sizeof(float) = " << sizeof(float) << '\n';
std::cout << "sizeof(double) = " << sizeof(double) << '\n';
return 0;
}Example output on Apple clang 21, arm64 macOS:
CHAR_BIT = 8
sizeof(char) = 1
sizeof(bool) = 1
sizeof(short) = 2
sizeof(int) = 4
sizeof(long) = 8
sizeof(long long) = 8
sizeof(float) = 4
sizeof(double) = 8Read that as a measurement of one machine, not as a table of C++ facts. Every number except sizeof(char) is this platform's choice. Run the program yourself; if you ever work on an embedded target, run it there too, because that is where the numbers start to differ.
Two things in that output are worth comment.
sizeof(long) and sizeof(long long) are both 8. The standard permits it, and the chain in §3 holds with equality.
sizeof(bool) is 1, even though a boolean needs one bit. There is no type smaller than char, because char is the unit of addressing. Chapter 3.6 returns to this.
Two small notes on the program itself. sizeof is an operator, not a function — the parentheses here are part of naming a type. And its result has a type of its own, std::size_t, which you will meet properly in Module 9; for now, it prints as a number.
5. What Common Platforms Actually Do
Between "the standard guarantees" and "my machine says" there is a third, useful category: what mainstream platforms have settled on. This is not a rule you can rely on in portable code, but knowing it tells you where the surprises are.
| Type | Linux / macOS, 64-bit | Windows, 64-bit |
|---|---|---|
char | 1 | 1 |
short | 2 | 2 |
int | 4 | 4 |
long | 8 | 4 |
long long | 8 | 8 |
One row differs, and it is the one that causes real bugs.
long is 8 bytes on 64-bit Linux and macOS and 4 bytes on 64-bit Windows. Both are fully conforming — the standard requires only at least 32 bits. Code that stores a 64-bit timestamp, a cycle count, or a large address in a long is correct on two of the three major platforms and silently truncating on the third.
This is the single most useful portability fact in the chapter, and it is also the argument for Chapter 3.4. When a width is part of a specification rather than a convenience, a type whose size varies by platform is the wrong tool, and C++ gives you types that say the width out loud.
6. Using sizeof Honestly
sizeof is the right tool for one job and the wrong tool for another, and the difference is worth stating.
Right: finding out what your current implementation does, so you can record a platform assumption deliberately.
Wrong: discovering a size and then hard-coding the number you saw.
// "I checked, int is 4 bytes here."
const int bytes_per_sample = 4; const std::size_t bytes_per_sample = sizeof(int);The second is right everywhere. The first is right until it is not, and it will not warn you when it stops being right.
7. Mistakes Worth Making Once
"int is 32 bits in C++." It is not — that is a property of mainstream platforms. The guarantee is at least 16 bits.
Assuming long is 64 bits. It is on Linux and macOS and is not on 64-bit Windows. This is a real, common, silent truncation.
Reading sizeof(char) == 1 as "a char is 8 bits". It means char is the unit sizeof counts in. CHAR_BIT answers the bits question.
Hard-coding a measured size. sizeof costs nothing at run time — it is computed during compilation. There is no reason to replace it with a literal.
Assuming sizeof tells you the number of meaningful bits. It tells you how much storage the object occupies. A bool takes a whole char of storage and carries one bit of information, and a type can have padding bits. Chapter 3.4 makes this precise for hardware fields.
Expecting sizeof(long) < sizeof(long long). The chain in §3 allows equality, and this machine shows both at 8.
8. What You Should Be Able to Say Now
- The fundamental types are the integer, character, boolean and floating-point types, plus
void. - C++ deliberately does not fix most type sizes, so the same language can target machines with different natural word sizes.
- The standard guarantees minimum ranges and a non-decreasing ordering from
charup tolong long— with≤, so equal sizes are allowed. sizeof(char)is exactly 1 by definition:charis the unitsizeofcounts in.- That is not a statement about bits.
CHAR_BITfrom<climits>gives the bits perchar, which must be at least 8. - A signed type and its unsigned counterpart are the same size.
- Measured on one machine:
CHAR_BIT8;char1,bool1,short2,int4,long8,long long8,float4,double8. These are that platform's choices, not C++ facts. longis 8 bytes on 64-bit Linux and macOS and 4 on 64-bit Windows. Both conform, and the difference silently truncates 64-bit values.- Use
sizeofrather than a measured literal — it costs nothing at run time and is correct everywhere. sizeofreports storage, not meaningful bits.- "C++ guarantees X" and "this platform does X" are different claims. Using the second while believing it is the first is the failure.
9. Exercises
Exercise 1 — guarantee or measurement? Classify each: (a) sizeof(char) == 1; (b) sizeof(int) == 4; (c) sizeof(short) <= sizeof(int); (d) CHAR_BIT == 8; (e) sizeof(unsigned int) == sizeof(int).
Exercise 2 — the truncation. A model stores a 64-bit cycle count in a long and is built on Linux and on 64-bit Windows. Describe what happens on each, say whether either compiler is wrong, and give the one-line fix.
Exercise 3 — defend the refusal. Explain in two sentences why C++ does not simply fix int at 32 bits. Your answer should mention the range of hardware it has to target.
Exercise 4 — what does 1 mean? A colleague says "sizeof(char) is 1, so a char is 8 bits." Explain exactly what is right and what is wrong in that sentence, and name the macro that answers the part they got wrong.
Exercise 5 — predict the ordering. Without running anything, say which of these must be true on every conforming implementation: (a) sizeof(short) < sizeof(int); (b) sizeof(short) <= sizeof(int); (c) sizeof(long) < sizeof(long long); (d) sizeof(long) <= sizeof(long long).
Exercise 6 — fix the literal. Rewrite so it is correct on any platform, and say what sizeof costs at run time:
const int buffer_bytes = 128 * 4; // 128 ints, 4 bytes eachExercise 7 — record the assumption. You measure sizeof(int) == 4 and your model depends on it. You are allowed to depend on it. Describe what you should write down, and where, so the dependency is found when the platform changes.
10. Summary
- The fundamental types are integer, character, boolean and floating-point, plus
void. - C++ fixes minimum ranges and an ordering, not exact sizes, so it can target machines with different word sizes.
sizeof(char)is 1 by definition —charis the unit.CHAR_BITgives the bits, and it is at least 8.- The ordering uses
≤: equal sizes are conforming, andlongandlong longwere both 8 on the machine measured. - Measured on one machine:
int4,long8,double8,bool1. Treat these as that platform's choices. longis 8 on 64-bit Linux/macOS and 4 on 64-bit Windows. Both conform; the difference truncates 64-bit values silently.- Use
sizeof, not a measured literal. It is free at run time and correct everywhere. sizeofreports storage, not meaningful bits.- Keep "C++ guarantees" and "this platform does" apart. Depending on the second is fine; mistaking it for the first is not.
The sizes are only half of what a type decides. The other half is how the bits in that storage are interpreted — and for integers, the biggest interpretation question is whether the topmost bit means a very large number or a negative one. That choice has a name, it changes the range you can represent, and it produces the single sharpest asymmetry in the language: one of the two kinds of integer has fully defined behaviour when a calculation goes out of range, and the other has none at all. Chapter 3.3 is about that difference and the bugs that come from not knowing it.
Standards & specifications
- Governing standard
- ISO/IEC 14882 (the C++ language standard)(opens ISO/IEC (JTC 1/SC 22/WG 21) in a new tab)
Defines the C++ language and its standard library — the type system and templates, the object model and object lifetime, initialisation and overload resolution, the memory model and the containers, algorithms and utilities the library provides. Like C's standard it is a FAMILY of editions (C++98/03, C++11, C++14, C++17, C++20, C++23) and the editions are NOT interchangeable: a feature this curriculum relies on may be unavailable, differently specified or merely proposed in another edition, so each lesson names the edition its claim depends on. It does not define any foreign-function interface to a hardware description language, nor what a compiler actually emits, nor platform behaviour on real silicon — those are the implementation's and the device's contracts. Working drafts published by WG21 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.
