Why Types Matter in C++
When you declare a value with an explicit type like int age = 30;, that type is not just a label - it tells the compiler how many bytes to reserve, how to interpret those bytes, and which operations are legal. Get the type wrong and you can silently lose precision, overflow, or invoke undefined behavior.
C++ groups its built-in types into a few families: integers, floating-point numbers, a character type, and a boolean. Let's look at each, then at the rules that trip people up.
The Fundamental Types
Here is one of each core type in a single program. Notice the literal suffixes (L, f, u) and the single quotes on char:
A bool prints as 1 or 0 by default, not true/false. A char uses single quotes - 'A' is one character, while "A" (double quotes) is a string literal, a completely different type. Those two mistakes are extremely common early on.
Sizes Are Not Fixed
This is the biggest surprise coming from languages like Java. The C++ standard only guarantees minimum sizes and a relative ordering (short ≤ int ≤ long ≤ long long). The actual size depends on your compiler and platform. Always check with sizeof:
On a typical 64-bit Linux build you'll see int = 4, long = 8. On 64-bit Windows, though, long is only 4 bytes. That portability gap is exactly why you should not write code that assumes long is 64-bit.
When you need an exact width, reach for the fixed-width integer types in <cstdint>:
Use int32_t/int64_t for file formats, network protocols, or anything that must behave identically across machines. Note the cast (int)a - streaming an 8-bit type prints it as a character, not a number, so cast it first.
Signed vs Unsigned
Every integer type comes in two flavors. A signed type can hold negatives; an unsigned type cannot, trading the negative range for a higher positive maximum. Plain int is signed by default.
Subtracting from an unsigned 0 wraps around to a huge positive number instead of going negative. This bites people constantly - especially with size_t (an unsigned type) returned by .size():
vector<int> v = {1, 2, 3};
// DANGER: v.size() is unsigned. If v is empty, v.size() - 1 wraps
// to a gigantic number and the loop runs nearly forever.
for (size_t i = 0; i <= v.size() - 1; i++) { /* ... */ }
Prefer i < v.size() (never <= size() - 1), or sidestep the whole issue with a range-based for loop.
Integer Overflow Is Undefined Behavior
Unlike unsigned wraparound (which is well-defined), signed integer overflow is undefined behavior in C++. The compiler is allowed to do anything - return garbage, optimize the check away, or crash:
The fix is the same as the overflow trap in any language: do the arithmetic in a wider type. Cast one operand to long long before the +, so the addition happens in 64 bits. Casting the result afterward is too late - the overflow already happened.
Picking the Right Type
For most code the defaults are fine: int for whole numbers, double for decimals. Reach for something else only when you have a reason.
| Type | Typical size | Use when |
|---|---|---|
int | 32-bit | The default for whole numbers |
long long | 64-bit | Values past ~2 billion: timestamps, large counters |
double | 64-bit | The default for decimals - good precision |
float | 32-bit | Memory-tight arrays where precision can suffer |
bool | 1 byte | A true/false flag |
int32_t / int64_t | exact | Cross-platform formats, protocols, bit manipulation |
A couple of gotchas to keep in mind. float has only about 7 significant decimal digits, so 0.1f + 0.2f is not exactly 0.3 - prefer double unless you truly need to save memory. And char may be signed or unsigned depending on the platform, so if you do arithmetic on raw bytes, spell out signed char or unsigned char.
Next: The auto Keyword
Writing out the type every time gets tedious, and sometimes the type is long or hard to name. C++ lets the compiler deduce it for you with the auto keyword - auto x = 42; makes x an int, and auto it = v.begin(); saves you from typing a verbose iterator type. The next page covers when auto makes code clearer and when it hides too much.
Frequently Asked Questions
What are the basic data types in C++?
The fundamental types are integers (short, int, long, long long), floating-point (float, double, long double), the character type char, and the boolean type bool. Each integer type can also be signed or unsigned. Everything else - std::string, arrays, your own classes - is built on top of these.
What is the difference between int and long in C++?
Both store whole numbers, but long is guaranteed to be at least as wide as int (often 64-bit on 64-bit platforms, but only 32-bit on Windows). The standard only fixes minimum sizes, so for a guaranteed width use the fixed-width types from <cstdint> like int32_t and int64_t.
How big is an int in C++?
The standard only guarantees int is at least 16 bits, but on virtually every modern desktop and server it is 32 bits. Because sizes are platform-dependent, never assume - print sizeof(int) to check, or use <cstdint> types like int32_t when you need an exact width.