1.4 Dynamic Memory — new, delete, and Allocation for Arrays
Memory Model in C++
A C++ program's memory is divided into several regions:
┌──────────────────────┐ High address
│ Stack │ ← Local vars, function call frames
│ ↓ │
│ │
│ ↑ │
│ Heap │ ← new / malloc allocations
├──────────────────────┤
│ BSS │ ← Uninitialized globals/statics (zero)
│ Data │ ← Initialized globals/statics
│ Text / Code │ ← Program instructions (read-only)
└──────────────────────┘ Low address
| Region | Stores | Lifetime | Management |
|---|---|---|---|
| Stack | Local variables, function frames | Function scope | Automatic |
| Heap | Dynamic allocations | Until manually freed | Manual |
| Data | Initialized globals/statics | Program lifetime | Automatic |
| BSS | Uninitialized globals/statics | Program lifetime | Automatic |
| Text | Executable code | Program lifetime | Read-only |
Why dynamic allocation?
- Size unknown at compile time — need to ask user for array size
- Lifetime must exceed scope — return an object from a function
- Large objects — stack has limited size (~8 MB typically)
- Polymorphism — base pointer to derived object
- Data structures — linked lists, trees, graphs
---
new Operator
The new operator allocates memory on the heap and returns a pointer to it.
Allocate a single value
int* p = new int; // allocate one int (uninitialised)
int* q = new int(42); // allocate one int, initialise to 42
int* r = new int{42}; // C++11 brace-init form
*p = 10;
cout << *p; // 10
Allocate an object
class Student {
public:
int id;
Student(int i) : id(i) {}
};
Student* s = new Student(101);
cout << s->id; // 101
Allocate an array
int* arr = new int[5]; // array of 5 ints (uninitialised)
int* arr2 = new int[5](); // array of 5 ints, zero-initialised
int* arr3 = new int[5]{1,2,3,4,5}; // brace-init (C++11+)
for (int i = 0; i < 5; i++) arr[i] = i + 1;
for (int i = 0; i < 5; i++) cout << arr[i] << " ";
---
delete Operator
Every new must be matched by a delete. Failing to delete = memory leak.
Delete single value
int* p = new int(42);
// ... use p
delete p; // frees memory; p is now a dangling pointer
p = nullptr; // optional but good practice
Delete array — note the brackets!
int* arr = new int[5];
// ... use arr
delete[] arr; // bracket form for arrays
Critical rule: Matchnewwithdeleteandnew[]withdelete[]. Usingdeleteon an array (ordelete[]on a single object) is undefined behaviour — may crash, may silently corrupt memory, may seem to work.
---
Common Errors
1. Memory leak
void leak() {
int* p = new int(42);
// forgot to delete — memory leaked
return; // pointer goes out of scope; memory still allocated
}
2. Dangling pointer
int* p = new int(42);
delete p;
cout << *p; // UB — pointer no longer valid
3. Double delete
int* p = new int(42);
delete p;
delete p; // UB — already freed
4. Wrong bracket form
int* arr = new int[10];
delete arr; // wrong — should be delete[] arr;
5. Out-of-bounds access
int* arr = new int[5];
arr[10] = 99; // UB — buffer overflow
---
new and delete vs malloc / free — exam classic
| Aspect | new / delete (C++) | malloc / free (C) |
|---|---|---|
| Language | C++ operator | C function |
| Header | None (built-in) | <cstdlib> / <stdlib.h> |
| Size calculation | Automatic (new int knows size) | Manual: malloc(sizeof(int)) |
| Return type | Typed pointer (e.g. int*) | void* — needs cast |
| Constructor / destructor | Calls constructor | Does NOT — returns raw memory |
| Failure | Throws std::bad_alloc | Returns NULL |
| Initialisation | Can use new int(42) to init | Returns uninitialised memory |
| Array form | new[] / delete[] | Same malloc/free |
| Overloadable | Yes (per-class) | No |
| Type safety | Strong | Weak (void*) |
Code comparison
malloc / free:
int* p = (int*) malloc(sizeof(int)); // cast needed
if (p == NULL) { /* failed */ }
*p = 42;
free(p);
new / delete:
int* p = new int(42); // size + init in one step
// (throws on failure)
delete p;
For classes, new does much more:
class A { public: A() { cout << "Constructor\n"; } };
A* a1 = new A(); // calls constructor
A* a2 = (A*) malloc(sizeof(A)); // does NOT call constructor
Using malloc for class objects is a bug — the object is not properly constructed.
---
Dynamic Memory for Arrays — Patterns
1. User-sized array
int n;
cout << "Enter size: ";
cin >> n;
int* arr = new int[n]; // size known only at runtime
for (int i = 0; i < n; i++) arr[i] = i * i;
for (int i = 0; i < n; i++) cout << arr[i] << " ";
delete[] arr;
2. 2D dynamic array
int rows = 3, cols = 4;
// Allocate
int** matrix = new int*[rows];
for (int i = 0; i < rows; i++) {
matrix[i] = new int[cols];
}
// Use
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
matrix[i][j] = i * cols + j;
// Free — IN REVERSE ORDER
for (int i = 0; i < rows; i++) delete[] matrix[i];
delete[] matrix;
3. Array of objects
class Point {
public:
int x, y;
Point() : x(0), y(0) {}
};
Point* points = new Point[10]; // calls default constructor 10 times
points[0].x = 5;
points[0].y = 7;
delete[] points; // calls destructor 10 times
---
Modern C++ — Smart Pointers (C++11+)
Manual new/delete is error-prone. Modern C++ uses smart pointers that auto-delete:
#include <memory>
// unique_ptr — single owner; automatically deleted
auto p = make_unique<int>(42);
cout << *p;
// no delete needed — destructor handles it
// shared_ptr — multiple owners with reference count
auto s = make_shared<Student>(101);
// auto-freed when last shared_ptr is destroyed
// unique_ptr for arrays
auto arr = make_unique<int[]>(10);
arr[0] = 1;
Modern note: The IPU exam expectsnew/deletestyle. In real-world code, preferunique_ptr/shared_ptrto eliminate memory leaks at the design level. RAII (Resource Acquisition Is Initialisation) is the C++ idiom for managing all resources.
---
Memory Leak Detection Tools
- Valgrind (Linux/Mac) — detects leaks, uninitialised reads, invalid frees
- AddressSanitizer (ASan) — Clang/GCC flag, run-time detection
- Visual Studio Debugger — built-in CRT debug heap
- Dr. Memory — Windows
- LeakSanitizer (LSan) — part of ASan
A leaking program may "work" for a short run but exhaust memory on long-running services (servers, embedded systems).
---
new Failure Handling
Default behaviour (throws)
try {
int* huge = new int[1000000000]; // may throw bad_alloc
} catch (bad_alloc& e) {
cout << "Allocation failed: " << e.what();
}
nothrow variant — returns nullptr instead of throwing
int* p = new(nothrow) int[1000000000];
if (p == nullptr) cout << "Allocation failed";
else { /* use p */ delete[] p; }
---
placement new (advanced)
Allocate the object in pre-existing memory:
char buffer[sizeof(Student)];
Student* s = new (buffer) Student(101); // constructs in 'buffer'
s->~Student(); // must manually call destructor — no delete
Used in: custom allocators, memory pools, embedded systems.
---
Study deep
- Memory leaks compound at scale. A single 100-byte leak in a function called once is invisible. The same leak in a server handling 10,000 requests/sec is 1 MB/sec leaked — server crashes within hours.
- RAII is C++'s killer feature. Java has garbage collection; C++ has RAII (objects clean up themselves on scope exit). RAII covers memory, files, locks, sockets, anything. Smart pointers are RAII for heap memory.
- Stack > Heap when possible. Stack allocation is essentially free (just a pointer adjustment). Heap allocation involves the allocator's internal bookkeeping. Modern advice: allocate on the stack unless you have a reason not to.
newcannot be ignored even with smart pointers. Smart pointers internally usenewanddelete. Understanding them is essential before using smart pointers correctly.
- C++11 introduced
make_unique/make_shared. These are preferred overneweven in modern C++ — they guarantee exception safety and reduce visible memory management. The IPU syllabus doesn't cover them, but every job interview expects familiarity.
Key Terms — Lesson 1.4
The terms below cover C++ memory management — every PYQ on new/delete, dynamic allocation, or memory pitfalls expects them.
Static Memory Allocation — Memory allocated at compile time for variables whose size is known at compile time — global variables, static locals, and stack-allocated local variables of fixed size. The compiler decides the layout; the developer cannot resize.
Dynamic Memory Allocation — Memory allocated at runtime from the heap using new (in C++) or malloc (in C). The developer chooses the size at the moment of allocation. Required when the size is not known at compile time.
Stack vs Heap — Two memory regions with different lifetimes and management. The stack holds function call frames and their local variables; allocation is automatic and very fast, deallocation happens at function return. The heap holds dynamically allocated objects; allocation is explicit (new/malloc), often slower, and deallocation must also be explicit (delete/free).
new Operator — The C++ operator that allocates memory on the heap, runs the constructor, and returns a typed pointer. int* p = new int(5); allocates an int initialised to 5. new throws std::bad_alloc on failure (or returns null with new(nothrow)).
delete Operator — The C++ operator that runs the destructor and deallocates heap memory previously allocated by new. delete p; deletes a single object; delete[] arr; deletes an array. Calling the wrong form is undefined behaviour.
new[] and delete[] — The array forms of new and delete. int* arr = new int[10]; allocates an array of 10 ints; delete[] arr; deallocates it. Mixing new with delete[] or new[] with delete is undefined behaviour — one of the classic C++ traps.
malloc / free — The C memory-management functions. malloc(size) allocates a raw byte block and returns void*; free(p) releases it. They do not call constructors or destructors — using them for C++ objects skips initialisation and cleanup. Only use malloc/free for primitive types or interoperability with C code.
Constructor Call (by new) — When new allocates an object, it automatically calls the constructor to initialise the object's state. This is the main reason to use new over malloc for C++ classes.
Destructor Call (by delete) — When delete deallocates an object, it automatically calls the destructor to clean up the object's resources (close files, release locks, free owned memory) before releasing the memory itself.
Memory Leak — A bug where dynamically allocated memory is not deallocated when no longer needed. Each leak permanently consumes a small amount of memory; over time leaks can exhaust available memory and crash the process. Tools: Valgrind, AddressSanitizer (ASan).
Dangling Pointer — A pointer that still holds the address of memory that has been deallocated. Dereferencing a dangling pointer is undefined behaviour — may crash, may read garbage, may corrupt unrelated data. Best practice: set pointers to nullptr after delete.
Double Free — Calling delete (or free) twice on the same pointer. Undefined behaviour — may crash, may corrupt the heap. Modern C++ smart pointers (std::unique_ptr, std::shared_ptr) make double free impossible.
nullptr (C++11) — The typed null-pointer constant in modern C++, replacing the older NULL macro. nullptr has type std::nullptr_t, which converts to any pointer type but never to integer. Resolves ambiguities that NULL (#defined to 0) caused with function overloading.
Heap Fragmentation — When repeated allocation and deallocation of varying-sized blocks leave the heap with many small free regions interspersed with allocated blocks, so that a large new allocation may fail even when the total free memory is sufficient. Long-running C++ services use pool allocators or arena allocators to mitigate fragmentation.
Smart Pointer (C++11) — A class template that wraps a raw pointer and manages its lifetime automatically via RAII. std::unique_ptr<T> owns the pointee exclusively and deletes it when the unique_ptr goes out of scope. std::shared_ptr<T> allows shared ownership via reference counting. std::weak_ptr<T> is a non-owning observer. Smart pointers eliminate most manual delete calls in modern C++.
make_unique / make_shared (C++14 / C++11) — Factory functions that construct an object on the heap and return a smart pointer: auto p = std::make_unique<MyClass>(args);. Preferred over raw new because they're exception-safe and require no manual delete.
RAII (Resource Acquisition Is Initialisation) — Bjarne Stroustrup's idiom for tying resource lifetimes to object lifetimes. The constructor acquires the resource (memory, file handle, lock); the destructor releases it. RAII makes resource management automatic and exception-safe and underlies the entire smart-pointer family.
Free Store / Heap — Two near-synonymous terms for the dynamically-allocated memory region. Strictly, "heap" is C terminology, "free store" is C++ terminology; in practice they refer to the same region.
std::bad_alloc — The exception type that new throws when memory allocation fails. Modern C++ programs catch std::bad_alloc if they want to handle out-of-memory gracefully; otherwise it propagates and terminates the program.
new (nothrow) — A variant of new that returns nullptr on failure instead of throwing std::bad_alloc. Used in code that prefers C-style error handling over exceptions.
Placement new — A specialised form of new that constructs an object at a specified memory address without allocating. Used for memory pools, in-place reconstruction, and embedded systems where the developer manages the memory layout manually.
Valgrind / AddressSanitizer (ASan) — Tools that detect memory bugs at runtime — leaks, double-frees, use-after-free, out-of-bounds reads/writes. Valgrind runs the program in a virtualised environment; AddressSanitizer is a compiler instrumentation (GCC/Clang) that's faster than Valgrind and now the industry default for memory bug detection.
---
PYQ pattern (very common): "Differentiatenew/deleteandmalloc/free." — Tabulate 6-8 differences (language, size calc, return type, ctor/dtor, failure, type safety). Show code comparison.
PYQ pattern: "Write a C++ program usingnewto dynamically allocate memory for an array of integers, take input from user and display sum." — Usenew int[n], loop input, loop sum,delete[].
PYQ pattern: "What is dynamic memory allocation? Explain with example." — Define heap allocation; use case (size unknown at compile time); shownew/deleteexample; mention need to free to avoid leaks.