Every C++ program starts here: how the machine remembers things, from raw bits in memory to the type system that keeps you safe.
You write int x = 42; and the compiler allocates 4 bytes of memory, stores the binary pattern for 42, and remembers that those bytes should be interpreted as a signed integer. Change the type to double and the same number 42 gets stored as 8 bytes in IEEE 754 floating-point format — a completely different bit pattern.
The type of a variable determines three things: how many bytes it occupies, how those bits are interpreted, and what operations are legal. Get the type wrong, and you get silent data corruption. A char can only hold values up to 127 (or 255 unsigned). Shove 300 into it, and the compiler will silently truncate — no warning, no error, just a wrong answer.
Type a number and see how it's stored as different types. Notice how the same value uses different bit patterns and different amounts of memory.
C++ inherits a set of built-in types (also called primitive types or arithmetic types) from C. These are the atomic building blocks: integers, floating-point numbers, characters, and booleans. Every other type in C++ is ultimately built from these.
The integer family comes in several sizes. The language guarantees minimum widths but not exact ones, which is why sizeof(int) can vary between platforms. On most modern 64-bit systems:
| Type | Typical Size | Range |
|---|---|---|
| char | 1 byte | -128 to 127 |
| short | 2 bytes | -32,768 to 32,767 |
| int | 4 bytes | -2.1B to 2.1B |
| long | 8 bytes | ±9.2 × 1018 |
| long long | 8 bytes | ±9.2 × 1018 |
Each integer type can be signed (default, holds negatives) or unsigned (non-negative only, doubles the positive range). An unsigned char holds 0–255 instead of -128–127.
| Type | Size | Precision |
|---|---|---|
| float | 4 bytes | ~7 decimal digits |
| double | 8 bytes | ~15 decimal digits |
| long double | 16 bytes | ~18 decimal digits |
int for integer arithmetic, double for floating-point. Avoid unsigned unless you need bit manipulation. Avoid float — the precision savings rarely justify the bugs.int occupy?A variable provides a named piece of storage. When you write int counter = 0;, three things happen: the compiler allocates 4 bytes on the stack, writes zero into those bytes, and records that the name counter refers to that location.
A definition allocates storage: int x = 5;. A declaration merely says "this name exists somewhere": extern int x;. You can declare a variable many times, but define it only once. This distinction becomes critical in multi-file programs.
C++ has four ways to initialize a variable, and they are not all equivalent:
c++ int a = 0; // copy initialization int b(0); // direct initialization int c{0}; // list (brace) initialization — C++11 int d = {0}; // copy list initialization — C++11
List initialization (with braces) is the safest: the compiler will refuse to narrow. Writing int x{3.14}; is a compile error because 3.14 would lose precision. The other forms silently truncate.
Click "Step" to execute each line. Watch variables appear and disappear on the stack.
int variable?The const qualifier is a promise to the compiler: "I will not modify this value after initialization." It sounds simple, but const interacts with references and pointers in subtle ways that trip up even experienced C++ programmers.
This is the key distinction. Top-level const means the object itself can't be changed. Low-level const means the thing being pointed or referred to can't be changed through this handle.
c++ int i = 42; const int ci = 42; // top-level: ci itself is const const int *p = &i; // low-level: *p is const, p is not int *const q = &i; // top-level: q itself is const const int *const r = &i; // both levels
The rule of thumb: const before the * applies to what's pointed to (low-level). const after the * applies to the pointer itself (top-level).
C++11 introduced constexpr to mark values that can be computed at compile time. A constexpr variable is implicitly const, but it also guarantees the value is known before the program runs.
c++ constexpr int size = 20; // evaluated at compile time constexpr int limit = size + 1; // also compile time int arr[limit]; // OK: limit is a constant expression
const says "I promise not to change this." constexpr says "this can be computed at compile time." Every constexpr is const, but not every const is constexpr.const int *p = &x;, what is const — the pointer or the data?A reference is an alternative name for an existing object. When you write int &r = x;, r becomes another name for x. It's not a copy — changing r changes x. Think of it as a sticky note placed on a box: the note isn't a new box, it's just another label on the same one.
References must be initialized when they are defined, and once bound, they cannot be rebound to a different object. This makes them much simpler (and safer) than pointers.
c++ int x = 10; int &ref = x; // ref is an alias for x ref = 20; // changes x to 20 // int &bad; // ERROR: references must be initialized // int &bad = 42; // ERROR: can't bind non-const ref to literal
A reference to const (often called a "const reference") can bind to a literal, an expression, or an object of a different type — things a plain reference cannot do:
c++ const int &r1 = 42; // OK: binds to a literal const int &r2 = x * 2; // OK: binds to a temporary const double &r3 = x; // OK: binds to a temporary double
const& avoids the copy while promising not to modify the original. This is the most common parameter-passing idiom in C++.Watch how modifying a reference affects the original, while modifying a copy does not.
A pointer holds the memory address of another object. Unlike a reference, a pointer is an object in its own right: it occupies memory (typically 8 bytes on a 64-bit system), it can be reassigned to point to different objects, and it can be null.
c++ int x = 42; int *p = &x; // p holds the address of x *p = 99; // dereference: changes x to 99 p = nullptr; // p now points to nothing
The * operator (dereference) follows the pointer to the object it points to. The & operator (address-of) gets the address of an object. They are inverses: *(&x) gives back x.
A null pointer does not point to any object. In modern C++, always use nullptr (not NULL or 0). Dereferencing a null pointer is undefined behavior — the program may crash, produce garbage, or appear to work fine until the worst possible moment.
Click operations to see how pointer operations change the memory layout.
*p = 99; do when p points to x?Now let's put it all together. Every C++ program has its memory divided into regions: the stack (local variables, function call frames), the heap (dynamically allocated memory), the data segment (globals and statics), and the code segment (the program instructions).
The simulation below lets you step through a small C++ program and see exactly where each variable lives, what it contains, and which pointers and references connect them.
Step through the code. The stack grows downward. Pointers are shown as arrows. Watch how const, references, and pointers interact in memory.
C++11 introduced auto and decltype to let the compiler figure out types for you. This isn't laziness — it makes code more maintainable and prevents errors when types are complex.
auto asks the compiler to deduce the type from the initializer:
c++ auto x = 42; // int auto pi = 3.14; // double auto s = "hello"; // const char* (surprise!) auto v = vec.begin();// vector<int>::iterator
auto drops top-level const and references. If you need them, say so explicitly:
c++ const int ci = 42; auto a = ci; // int, NOT const int (top-level const dropped) const auto b = ci; // const int auto &c = ci; // const int& (low-level const preserved)
decltype returns the exact type of an expression, including references and const. It does not evaluate the expression — it just inspects its type.
c++ int x = 5; decltype(x) y = x; // int (x is a variable) decltype((x)) z = x; // int& (parenthesized = lvalue expression)
decltype(x) gives the declared type of variable x. But decltype((x)) treats x as an expression, and since it's an lvalue, the result is a reference. This subtle difference has bitten many a C++ programmer.const int ci = 0; auto x = ci; — what is the type of x?This chapter covered the foundation of everything in C++. Every class, every template, every algorithm ultimately manipulates objects with types stored in memory. The concepts here — especially pointers, references, and const — will recur in every subsequent chapter.
| This Chapter | Builds Toward |
|---|---|
| Primitive types | Class types (Ch 7), template parameters (Ch 16) |
| const | const member functions, constexpr (Ch 7) |
| References | Pass by reference (Ch 6), range-for, move references (Ch 13) |
| Pointers | Dynamic memory (Ch 12), smart pointers, polymorphism (Ch 15) |
| auto / decltype | Lambda captures, template type deduction (Ch 16) |
Next up: Chapter 3: Strings, Vectors, and Arrays — where we use these basic types to build the first real data structures.