Variables and Memory
What variables really are — names for boxes in memory — and how the compiler tracks them.
In every language, a variable is just a name you give to a place in memory. In C++ that statement is almost literally true. When you write
int x = 42;you are telling the compiler:
- Reserve enough memory for an
int(typically 4 bytes). - Remember that the name
xrefers to that piece of memory. - Initialize it to the value
42.
You can later read the variable (std::cout << x;) or write
to it (x = 99;). The name x stays bound to the same place in
memory for as long as the variable is alive.
Declarations vs definitions vs assignments
These three words sound similar but mean different things.
| Code | What it does |
|---|---|
int x; | Definition. Reserves memory; the value is uninitialized (garbage). |
int x = 42; | Definition + initialization. Reserves memory and gives it the value 42. |
extern int x; | Declaration only. "Promise: an int named x is defined somewhere else." |
x = 99; | Assignment. x must already exist; overwrite its value. |
Beginners almost never write the third form, but they care a lot about the difference between the second and the fourth: creating a variable vs changing one.
Always initialize
C++ does not zero-initialize local variables by default. If you forget to give a local variable a value, reading it is undefined behavior — the compiler is allowed to do anything in response, including making your program appear to work in testing and fail in production. Make this a habit:
Modern C++ tip: write int x{}; (called value initialization)
when you want a default zero. The empty braces are the safest way
to ask for a sensible starting value.
Types are promises about memory
Every variable in C++ has a type. The type is not just a label; it tells the compiler:
- How much memory to reserve. An
intis 4 bytes (typically), adoubleis 8 bytes, acharis 1 byte. - How to interpret those bytes. The same 4 bytes might encode an integer, a float, or four ASCII characters; the type tells the compiler which interpretation to use.
- What operations are allowed. You can add two
ints. You can index into anint*. You cannot multiply twostd::strings.
This is why C++ is called a statically typed language: every
variable's type is decided at compile time and the compiler enforces
it. You cannot do x = "hello"; if x is an int. The compiler
catches the bug before your program ever runs.
A tour of fundamental types
You will use these constantly:
| Type | What it stores | Typical size | Range / notes |
|---|---|---|---|
bool | true / false | 1 byte | yes/no |
char | one character or small integer | 1 byte | -128..127 or 0..255 |
int | integer | 4 bytes | about ±2.1 billion |
long | larger integer | 4 or 8 bytes | platform-dependent |
long long | even larger | 8 bytes | about ±9.2 quintillion |
unsigned int | non-negative integer | 4 bytes | 0..4.29 billion |
float | single-precision floating-point | 4 bytes | ~7 decimal digits |
double | double-precision floating-point | 8 bytes | ~15 decimal digits |
std::string | growable string of characters | depends | from <string> |
When you don't know which integer type to use, reach for int. When
you need to be precise about width (say, a 16-bit network protocol
field), use <cstdint> types like int16_t, int32_t, int64_t.
auto: let the compiler infer the type
Modern C++ lets you write auto instead of spelling the type out,
and the compiler will fill in the right type by looking at the
initializer. This is handy for long type names.
auto does not mean "no type." It means "you, the compiler,
figure out the type and lock it in." x is just as much an int
as if you had written int x;.
Scope: where a variable is visible
A variable's scope is the region of source code where its name
makes sense. The most common rule: a variable's scope starts at its
declaration and ends at the closing } of the smallest enclosing
block.
Smaller scopes are better. Declare each variable as close to its first use as possible, and the program becomes much easier to read and reason about.
Lifetime: when a variable lives and dies
Closely related to scope is lifetime: the period of time during execution when the variable's memory is valid.
For a local variable, the lifetime equals the scope: the variable is
born when execution reaches its declaration and dies at the closing
} of its block. For a global variable (declared outside any
function) the lifetime is the entire program. For a heap-allocated
object (with new) the lifetime is whatever you say; we will
spend whole chapters on that.
A common bug is returning a pointer or reference to a local variable: by the time the caller uses it, the variable has been destroyed.
int* bad() {
int x = 42;
return &x; // returning the address of a local --
} // -- x dies HERE, the pointer dangles.This is undefined behavior. We will cover pointers, references, and how to avoid this trap properly in upcoming chapters.
const: variables that don't vary
If a "variable" is never supposed to change, mark it const. The
compiler will enforce that, and you'll never have an accidental
overwrite.
A useful habit: declare everything const by default, and only
remove const from things that genuinely need to change. Most of
your variables will turn out to be effectively constant once you
inspect them.
Implicit conversions and a famous footgun
C++ will sometimes silently convert one type to another. Most are
harmless (int to double); some are dangerous (double to int
truncates, signed to unsigned wraps).
Modern advice: prefer static_cast<T>(value) when you genuinely want
a conversion. It makes the intent explicit and easier to grep for.
Challenge: variable practice
Implement celsius_to_fahrenheit so the program prints 98.6 (no trailing newline, just 98.6).
The formula is F = C * 9/5 + 32. Watch out: 9/5 in integer arithmetic is 1, not 1.8!
Test your understanding
In C++, what is a variable, in the simplest physical sense?
A label attached to a function.
A copy of data stored in the CPU's registers permanently.
A name bound to a region of memory of a known type and size.
A pointer to a row in a database table.
What happens if you read a local variable that you never initialized?
It is always zero.
The compiler refuses to compile the program.
You get undefined behavior: the bytes in that memory are whatever happened to be there, and any code that uses them may misbehave.
The runtime throws a NullVariableException.
What does const do to a variable?
It makes the variable live in the heap.
It guarantees the variable will be optimized away.
It tells the compiler that this variable's value must not change after initialization, and prevents any code that tries to.
It exports the variable to other source files.
Why is auto not a violation of static typing?
Because auto makes the variable dynamically typed.
Because the compiler still determines a single concrete type at compile time and locks it in; auto only saves you from spelling the type out.
Because auto only works in templates.
Because the type is decided at runtime based on what you assign first.
Next: a closer look at types themselves — what kinds C++ has, and how to choose between them.