Lecture 4
In this lecture, we'll cover memory, references, and pointers. These are integral concepts in C++. Not building intuition for these concepts will make the rest of this class miserable.
Memory
In Python, you never had to think about where a variable "lives." The interpreter handled all of that for you — you wrote x = 5 and Python quietly managed memory behind the scenes.
In C++, we have to understand what actually happens when we write int x = 5;. Every variable takes up space somewhere in memory, and where it lives matters.
The Stack
When your program runs, the operating system gives it a region of memory called the stack. Local variables — the ones you declare inside main() or any function — live here. Each variable occupies a fixed amount of space determined by its type.
Here's what the stack looks like after running these three declarations:
Every variable has an address — a unique location in memory — and a value stored at that address. The address is just a number that tells the hardware where to find the data.
You can query how much space a type takes up using sizeof():
The exact sizes depend on your compiler and platform, but on most modern systems int is 4 bytes
and double is 8 bytes. char is guaranteed to always be 1 byte by the C++ standard.
The Heap
There's another region of memory called the heap. While the stack is used for local variables with known lifetimes, the heap is used for data that needs to survive beyond the function that created it, or when you don't know the size at compile time.
We won't be working with the heap directly in this lecture — that comes later. For now, just know that it exists as a separate, larger pool of memory that programs can draw from on demand.
References
Now that we know every variable has an address and a value, we can talk about references.
A reference is an alias — another name for the same memory location. When you create a reference to a variable, you're not creating a copy; you're giving the same box a second label.
Both x and ref now refer to the exact same box in memory:
Because they share the same address, mutating one mutates the other:
This matters a lot when we get to functions. Passing a variable by reference means the function works on the original — no copy made. We'll return to this in Lecture 5.
You cannot declare a reference without binding it to a variable. int& ref; is a compile error.
References also cannot be reseated — once bound to a variable, they always refer to that same
variable for their entire lifetime.
Pointers
A pointer is a variable that stores the address of another variable. While a reference is just another name for the same box, a pointer is its own box whose value is an address.
Declaring and using pointers
The * in a type declaration means "pointer to":
The & here is the address-of operator — it gives you the address of a variable. ptr is now a separate variable on the stack whose value is x's address:
ptr holds x's address. To read or write the value at that address, we dereference with *:
Pointer arithmetic
Because pointers store addresses — and addresses are just numbers — you can do arithmetic on them. Adding 1 to a pointer advances it by sizeof(T) bytes, where T is the type the pointer points to.
This is why sizeof() matters: the compiler uses it to calculate how many bytes to skip when you write ptr + 1.
nullptr
An uninitialized pointer holds whatever garbage value happens to be in memory — and dereferencing it is undefined behavior (usually a crash). Always initialize a pointer to nullptr when you don't have a value to point to yet:
Dereferencing nullptr is undefined behavior and almost always crashes your program immediately. Before dereferencing a pointer you're not certain about, check that it isn't nullptr first:
References vs. Pointers
Now that you've seen both, here's how they compare:
| Reference | Pointer | |
|---|---|---|
| What it stores | An alias (same address as original) | An address |
| Can be null | No — must bind to a variable | Yes (nullptr) |
| Can be reseated | No — always refers to same variable | Yes — can point elsewhere |
| Dereferencing syntax | None — use it like the original variable | *ptr |
| Must initialize at declaration | Yes | No, but always should (nullptr) |
Prefer references when you know you always have a valid variable and won't need to change what you're referring to. Use pointers when you need nullability, reseating, or arithmetic.
Exercises
Q1.
Without running the code, predict what this program prints. Then verify.
Q2.
Without running the code, predict the output. Pay close attention to what * and & do on each line.
Q3.
Given an array and a pointer to its first element, predict what this program prints. Recall that ptr + n advances by n * sizeof(int) bytes.
Q4.
Write a program that declares two int variables a and b, then swaps their values using only pointers — no direct assignment like a = b is allowed. Use two int* pointers and a temporary int to perform the swap.
Example output:
Practice Problems
- Reverse String — Easy
- Reverse Linked List — Easy
- Middle of the Linked List — Easy