cadence_learn
the_c++_track / level 10 of 15 ▶ 16 min

numbered from zero.

One name for a whole row of values. Slide the index past the end and watch a real program hand you memory that was never yours — with no error at all.

after this level you'll be able to
  • Read and write any element of an array by index
  • Say why the last index is size − 1 and what happens past it
  • Choose between an array and a vector, and loop over either
many things, one name

numbered boxes in a row

So far one name has meant one value. An array is one name for a whole row of them, and you pick which one with a number in square brackets.

int a[5] = {10, 20, 30, 40, 50}; cout << a[0]; // 10 — the FIRST one is number 0 cout << a[4]; // 50 — the last one is 4, not 5

Counting from zero looks like a quirk until you notice it makes the loop from level 7 fit perfectly: for (int i = 0; i < 5; i++) visits every box and stops in exactly the right place.

why the last index is not the size
int a[5] — FIVE BOXES, NUMBERED FROM ZERO 10 a[0] 20 a[1] 30 a[2] 40 a[3] 50 a[4] — THE LAST ? a[5] — NOT YOURS whatever memory happens to be next Five boxes, numbered 0 to 4. The last index is always size − 1, which is why loops are written i < size and never i <= size. Reading a[5] does not crash and does not warn at runtime. Verified: it printed 1 — a number that was simply lying around.
Off the end is not an error. It is just somewhere else. C++ hands you whatever is in that memory, and the program carries on as if nothing happened — which is why level 7’s i < size habit matters here.
try it yourself

walk off the end on purpose

▤ lab 09 · the index

Slide past 4 and see what a real program does.

a[0]
Slide the index. The first box is 0 — get used to that now and it stops being strange.
the mistake nothing stops

Reading past the end does not crash and does not warn at runtime. Verified on a real program: a[5] printed 1 — a value that simply happened to be sitting in the next piece of memory. Writing past the end is worse: you are changing memory belonging to something else, and the damage shows up somewhere unrelated, later. This is the single most notorious bug class in C and C++.

The compiler does warn when it can see the number you typed, like a[5]. It cannot warn when the index is a variable — which is where every real bug of this kind lives.

◈ ask an ai about this

“Why do C++ arrays start at index 0, and what actually happens if I read past the end?”

chatgpt ↗ claude ↗

the better one

vector, which knows its own size

An array has one enormous limitation: its size is fixed when you write it, and it does not know how big it is. A vector fixes both.

#include <vector> vector<int> v = {1, 2, 3}; v.push_back(4); // now four long — it grew cout << v.size(); // 4 — it knows cout << v[3]; // 4 — same square brackets
array — int a[5]
  • Size fixed forever, decided when you write it
  • Does not know its own length
  • What your textbook teaches first
vector<int> v
  • Grows with push_back
  • .size() tells you how many
  • What you would actually use

Your course will teach arrays first, because they are what is underneath and because pointers in level 13 make more sense once you have seen them. Use whichever your assignment asks for — and note that v.size() in the loop condition removes a whole category of off-by-one, because the number is never written down twice.

◈ ask an ai about this

“What is the difference between an array and a vector in C++, and when should I use each?”

chatgpt ↗ claude ↗

the shape you will write constantly

visit every one

for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; }

Learn that as one unit. It is level 5’s “find the biggest” shape, level 7’s loop and this level’s indexing, all at once — and it is most of what a first course asks you to do with a list.

a grid

two dimensions — a list of lists

A seating plan. A noughts-and-crosses board. Marks for five students across four assignments. When your data has rows and columns, you give the array two sets of brackets:

int grid[3][4]; // 3 rows, 4 columns — 12 boxes in total grid[2][3] = 23; // row 2, column 3 — BOTH count from zero

Read it as [row][column], in that order, and both still start at zero — so int grid[3][4] has rows 0 to 2 and columns 0 to 3. There is no grid[3] and no [4], and going past either end is as silent as it was in one dimension.

To visit every box you need level 8’s loop inside another one — the outer walks the rows, the inner walks the columns of that row:

for (int r = 0; r < 3; r++) { for (int c = 0; c < 4; c++) { cout << grid[r][c] << "\t"; } cout << endl; // end of a row — OUTSIDE the inner loop }
output
0   1   2   3
10  11  12  13
20  21  22  23
the line that decides whether it looks like a grid

cout << endl; belongs to the outer loop, after the inner one finishes. Put it inside the inner loop and every single number lands on its own line; leave it out altogether and all twelve run together in one long row. Neither is an error and neither warns — you just get a mess, and the fix is one line moving by one level of indentation.

The nesting is worth pausing on: the inner loop runs completely, all four columns, for every single lap of the outer one. Three rows × four columns is twelve laps of the inner body. Trace the first two rows by hand — level 4, with two counters instead of one — and nested loops stop being confusing for good.

do this together

be the array

☶ two people · 12 minutes · six cups in a row

Number the cups 0 to 5 out loud. Then ask for things by number.

person a

Ask for values by index. “Give me a[0].” “Give me a[5].” Then ask for a[6] and see what B does.

person b — the array

Hand over whatever is in that cup. For a[6], do not refuse — reach past the end of the row and grab whatever object is there. That is exactly what C++ does.

Then run level 5’s find-the-biggest on the cups, out loud, using only “best so far”. You will find you already know the algorithm and only needed the notation — which is the point of having done the thinking track first.

what to keep

three things worth remembering

01

The last one is size − 1

Five boxes are numbered 0 to 4. This is why loops use < and not <=.

02

Off the end is silent

No crash, no warning at runtime. You get someone else’s memory and carry on.

03

Let it tell you its size

i < v.size() beats a number you typed, because the number can go stale.

check yourself

4 questions before you move on

Not recall — these are the shapes an exam actually uses. Every answer below was produced by compiling and running the code, so if you disagree with one, the compiler is the one to believe.

▢ check yourself4 questions

Have a real go before revealing. Being wrong here is worth more than being right in three weeks.

01

What does this print?

int a[5] = {10,20,30,40,50}; cout << a[4];
02

What happens if you read a[5] on that same array while the program runs?

03

A 2 by 3 grid where each cell holds row×10 + column. What is g[1][2]?

int g[2][3]; for(int r=0;r<2;r++) for(int c=0;c<3;c++) g[r][c]=r*10+c; cout << g[1][2];
04

In a nested loop printing a grid, where does cout << endl; belong?

answered: 0 of 4right first time: 0
Stuck? Peter reads these personally and replies to your email.
Ask Peter →