The heart of C++: references and raw pointers, the stack-versus-heap memory model, new/delete and object lifetime, and how dangling pointers and memory leaks happen.
Local variables live on the stack — created on entry, destroyed automatically when the scope ends. The heap is memory you request explicitly and must return explicitly. A pointer holds the address of an object; & takes an address, * dereferences it.
#include <iostream>
int main() {
int x = 42; // on the stack
int* p = &x; // p holds the address of x
std::cout << *p << "\n"; // 42 — dereference to read the value
*p = 99; // write through the pointer
std::cout << x << "\n"; // 99 — x changed
int* nothing = nullptr; // points at nothing; never dereference this
if (nothing) *nothing = 1; // guard before use
return 0;
}new allocates on the heap and returns a pointer; delete frees it. Heap objects live until you delete them — their lifetime is in your hands. Every new needs exactly one matching delete (and new[] pairs with delete[]).
#include <iostream>
int main() {
int* arr = new int[3]{10, 20, 30}; // heap array
std::cout << arr[1] << "\n"; // 20
delete[] arr; // release — required
struct Node { int value; Node* next; };
Node* head = new Node{1, nullptr};
std::cout << head->value << "\n"; // 1 — -> dereferences + accesses
delete head;
return 0;
}Two classic bugs. A leak is heap memory you allocated but never freed — it accumulates until the program exhausts memory. A dangling pointer still points at memory that was already freed; using it is undefined behavior. These bugs are exactly why smart pointers exist (next lesson).
void leak() {
int* p = new int(5);
// ... function returns without delete -> the 4 bytes are lost forever
}
int* dangling() {
int local = 10;
return &local; // BUG: 'local' dies when the function returns;
// the caller gets an address to freed stack memory
}
void useAfterFree() {
int* p = new int(1);
delete p; // freed
// *p = 2; // BUG: dangling — writing to freed heap memory
}