Lecture 6
C-Style Arrays
An array is a fixed-size, contiguous block of elements all of the same type. You declare one by specifying the type, name, and size:
Array indexing is zero-based. primes[0] is the first element, primes[3] is the last.
C++ does not check array bounds. Accessing primes[4] or primes[-1] is undefined behavior — no
error, no exception, just silent data corruption or a crash.
Arrays and pointers
In Lecture 4 we used pointer arithmetic to walk through an array. That works because an array name in C++ decays to a pointer to its first element.
ptr[i] is exactly *(ptr + i) — the two notations are interchangeable.
The array name itself is not a pointer variable — you can't reassign it (nums = ptr is a compile
error). But it converts to a pointer whenever a pointer is expected.
Passing arrays to functions
When you pass an array to a function, it decays to a pointer and the size information is lost. You have to track the size separately:
Dynamic Allocation: new and delete
Stack variables live and die with their enclosing function. Sometimes you need memory that:
- outlives the function that created it, or
- has a size you don't know until runtime
For those cases, you allocate on the heap using new.
new returns a pointer to the allocated memory. delete releases it. After delete, the pointer
is a dangling pointer — it points to freed memory. Setting it to nullptr immediately is
defensive practice.
If you lose the pointer before calling delete, the memory is never freed — that's a memory
leak. The program won't crash immediately, but long-running programs can exhaust available
memory.
Modern C++ has smart pointers (std::unique_ptr, std::shared_ptr) that call delete
automatically when they go out of scope, eliminating most manual memory management. They're beyond
the scope of this course, but worth knowing they exist.
Dynamic arrays: new[] and delete[]
To allocate an array whose size is known only at runtime, use new[]:
delete frees a single object. delete[] frees an array. Mixing them is undefined behavior.
Multi-Dimensional Arrays
Stack-allocated (fixed size)
A 2D array on the stack is declared with two size brackets:
Elements are stored row-major in memory — all elements of row 0, then all of row 1, and so on:
Heap-allocated (dynamic size)
When dimensions are only known at runtime, the standard approach is an array of pointers — each pointing to one row:
The cleanup order matters: delete each row before deleting the array of row pointers. Reversing the order leaves the row allocations unreachable — a memory leak.
Exercises
Q1.
Without running the code, predict what this program prints. Then verify.
Q2.
Without running the code, predict what this program prints.
Q3.
Trace through this program step by step. At each checkpoint, describe what is in memory.
Q4.
Each snippet below has a bug. For each one, identify what's wrong and what the consequence is.
Q5.
Write a function make_range(int n) that allocates an int array of size n on the heap, fills it
with values 0, 1, 2, ..., n-1, and returns the pointer. The caller is responsible for freeing the
memory. In main, call make_range(5), print all elements, then free the array.
Q6.
Write a function make_identity(int n) that allocates an n × n identity matrix on the heap (1s
on the diagonal, 0s elsewhere) and returns it as an int**. In main, call it with n = 3, print
the matrix row by row, then free all memory in the correct order.
Practice Problems
- Contains Duplicate — Easy
- Best Time to Buy and Sell Stock — Easy
- Maximum Subarray — Medium