Pointers & Memory
The module that defines C++. No garbage collector stands between you and memory, so you get direct control and, if you're careless, direct responsibility for every leak and dangling pointer.
Pointers
A pointer is a variable that holds a memory address. &x takes the address of x; *p dereferences the pointer to reach the value it points at. A pointer can be reassigned or set to nullptr — the ways it differs from a reference.
int x = 10; int* p = &x; // p holds the ADDRESS of x *p; // 10 — dereference: the value at that address *p = 20; // writes through the pointer std::cout << x; // 20 — x changed p = nullptr; // points at nothing (use nullptr, not NULL or 0) if (p) *p; // guard before dereferencing // pointer to a struct — arrow syntax std::string s = "hi"; std::string* sp = &s; sp->length(); // 2 — (*sp).length() shorthand
Pointer int* | Reference int& | |
|---|---|---|
| Can be null? | Yes (nullptr) | No — must bind to something |
| Can be reassigned? | Yes, points elsewhere | No — bound for life |
| Syntax to read | *p | ref (just use it) |
| Use when | Optional / rebindable | Always-valid alias |
Stack vs Heap
Local variables live on the stack: automatically created and destroyed as functions enter and exit, and fast. The heap is memory you request explicitly and must return yourself — bigger, flexible, but manual.
void demo() { int stackVar = 42; // STACK — destroyed when demo() returns std::vector<int> v(100); // v is on the stack, but its 100 ints live on the heap int* heapVar = new int(42); // HEAP — survives until you delete it // ... use *heapVar ... delete heapVar; // YOU must free it — no GC } // stackVar and v are cleaned up automatically here
Prefer the stack
Stack allocation is automatic, fast, and leak-proof. Reach for the heap (new) only when an object must outlive the scope that created it or is too large for the stack. In modern C++, even then you'd use a smart pointer, not raw new.
new & delete
new allocates on the heap and returns a pointer; delete frees it. Every new needs exactly one matching delete, and array allocations use the new[] / delete[] pair.
// single object int* p = new int(5); delete p; // free it p = nullptr; // avoid a dangling pointer // array — note the [] on both sides int* arr = new int[100]; arr[0] = 1; delete[] arr; // delete[] for arrays — mismatch is undefined behavior
delete on a new[] allocation, or delete[] on a single new, corrupts the heap. In modern C++ you avoid the problem entirely by not writing raw new at all.Leaks & Dangling Pointers
These two bugs are the price of manual memory, and the reason smart pointers exist.
// MEMORY LEAK — new without delete, memory never returned void leak() { int* p = new int(5); return; // p is lost, its heap memory is leaked forever } // DANGLING POINTER — using memory after it's freed int* p = new int(5); delete p; *p = 10; // UNDEFINED BEHAVIOR — p points at freed memory // DOUBLE FREE — deleting twice, also undefined behavior delete p; // already deleted above
unique_ptr, shared_ptr), covered in the Modern C++ module. They free memory automatically when they go out of scope — RAII doing the work the garbage collector does in other languages.