Reading — step 1 of 7
Learn
~2 min readPointers and Memory
C++ programs allocate memory in two places:
- The stack — automatic variables, function parameters. Fast (just a pointer bump). Limited size (~1 MB typical). Variables die when their scope ends.
- The heap — explicit allocations via
new. Larger pool. YOU manage the lifetime — mustdeletewhat younew'd, or memory leaks.
void stackExample() {
int x = 5; // on the stack
int arr[1000]; // on the stack — careful, 1MB stack limit
} // x and arr automatically destroyed
void heapExample() {
int* p = new int(42); // heap allocation, initialized to 42
std::cout << *p;
delete p; // explicit free — pointer is now dangling
// *p = 5; // undefined behavior — already freed
}
new returns a pointer to a heap-allocated object. delete frees it. Forgetting delete is a memory leak. Calling delete twice is undefined behavior. Calling delete on a non-heap pointer is undefined behavior.
Heap arrays
For arrays:
int* arr = new int[100]; // 100 ints on the heap, uninitialized
arr[0] = 1;
arr[99] = 99;
delete[] arr; // note the [] — different from regular delete
Array new requires array delete[]. Mismatch is undefined behavior. Modern code prefers std::vector for dynamic arrays (handles all this for you).
When to use new (rarely, in modern C++)
In modern C++, you almost never write raw new. Reasons:
- Manual memory management is error-prone — leaks, double-frees, dangling pointers.
- Smart pointers (next lesson) handle ownership automatically.
- STL containers manage heap memory internally.
// Old way (error-prone):
std::string* p = new std::string("hello");
// ... lots of code ... did everyone remember to delete?
delete p;
// Modern way (RAII):
auto p = std::make_unique<std::string>("hello");
// p auto-deletes when it goes out of scope
// For dynamic arrays:
std::vector<int> v(100); // handles allocation and freeing for you
Use new only for:
- Educational purposes (this lesson)
- Implementing smart pointers / containers
- Interop with C APIs that take ownership of raw pointers
What's actually on the heap?
When you write new T(args), the runtime:
- Asks the OS for
sizeof(T)bytes of heap memory. - Calls T's constructor in that memory.
- Returns a pointer to it.
When you write delete p:
- Calls T's destructor on
*p. - Returns the memory to the OS allocator.
Common mistakes
- Forgetting
delete→ memory leak. Heap memory persists until process exits or you free it. - Calling
deletetwice → double-free → corruption + undefined behavior. Use smart pointers to avoid. - Dereferencing after
delete→ use-after-free → undefined behavior. - Mixing
newwithfreeormallocwithdelete→ undefined behavior. They're incompatible. - Mixing
new Twithdelete[] p(or vice versa) → undefined behavior.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…