Arrays in C++: Sequential Storage, Indexing and Worked Examples

Build one C++ array from declaration to traversal, updates, memory addresses, fixed-capacity insertion and deletion, then extend the model to two dimensions.

KnowledgeGate Team

Exam prep & CS education

Updated 27 Aug 20265 min read

You can write int values[6], yet still confuse the sixth element with index 6, call every access fast, or use an address formula without checking the element size. Memory addresses advance by element size; insertion and deletion may shift elements; row-major storage lays out rows in sequence. Use Coding & Skills to browse related learning paths as you practise, and test each pattern with pencil and paper.

What an array organises, and why sequential matters

int values[6] = {14, 9, 21, 6, 18, 11};

It holds six int elements contiguously. Indices are 0 through 5: values[0] is 14, values[2] is 21, and values[5] is 11. Order tracks positions; addresses track bytes.

Equal sizes enable base-plus-offset indexing. With sizeof(int) == 4, element i has offset 4i bytes. Integer size varies, so use sizeof(int). Its compile-time extent is fixed; it cannot grow. Indexing is constant time; search may inspect every element.

Declaration, initialisation, traversal and updates

int a[4] = {5, 8, 13, 21}; // {5, 8, 13, 21}
int b[4] = {5, 8};         // {5, 8, 0, 0}
int c[4]{};                // four zeros
int d[] = {3, 1, 4};       // inferred extent: 3
int uninitialised[4];      // write before reading
for (std::size_t i = 0; i < 6; ++i)
    std::cout << '(' << i << ',' << values[i] << ") ";

Output: (0,14) (1,9) (2,21) (3,6) (4,18) (5,11). Use i < 6; i <= 6 reaches invalid values[6]. For values alone: for (int value : values).

Sum trace: 0 -> 14 -> 23 -> 44 -> 50 -> 68 -> 79. Maximum trace: 14 -> 21, ending at index 2. After values[3] = 16, the array is {14, 9, 21, 16, 18, 11} and its sum is 89.

Worked example: every address in a six-element array

Assume &values[0] is 1000 and sizeof(int) == 4. Element i follows i equal elements:

address(values[i]) = base + i * sizeof(int) = 1000 + i * 4

i

values[i]

Byte offset

Hypothetical address

0

14

0

1000

1

9

4

1004

2

21

8

1008

3

6

12

1012

4

18

16

1016

5

11

20

1020

For values[4], 1000 + 4 * 4 = 1016, holding 18. Here values + 4 equals &values[4], while *(values + 4) reads 18. Pointers in C for GATE teaches this model in C; it also underpins C++ arrays.

Six-element int array with indices 0 to 5, its values, and byte addresses 1000 to 1020, showing values[4] at address 1016.

Insertion and deletion with fixed capacity

int values[8] = {14, 9, 21, 6, 18, 11};
std::size_t n = 6;

Capacity is eight; logical size is six. Insertion requires n < 8 and pos in 0..n. To insert 15 at pos = 2, shift right:

for (std::size_t i = n; i > pos; --i)
    values[i] = values[i - 1];
values[pos] = 15;
++n;

Copy 5 -> 6 (11), 4 -> 5 (18), 3 -> 4 (6), and 2 -> 3 (21). Result: {14, 9, 15, 21, 6, 18, 11}, with n = 7.

Delete index 4, removing 6. Shift 5 -> 4 (18) and 6 -> 5 (11), then decrement n. Result: {14, 9, 15, 21, 18, 11}; ignore the stale outside value. pos = n appends without shifts; deletion at pos >= n is invalid. Raw arrays check neither. Middle insertion or deletion may move a suffix; indexing does not.

Two-dimensional arrays and row-major addresses

int grid[2][3] = {{4, 7, 1}, {9, 2, 6}};

Rows are 0..1; columns are 0..2. Nested loops using outer r < 2 and inner c < 3 print:

4 7 1
9 2 6

Assume &grid[0][0] = 2000 and sizeof(int) == 4. Row-major storage places each three-integer row in sequence:

address(grid[r][c]) = 2000 + ((r * 3) + c) * 4

For grid[1][2]: linear index 1 * 3 + 2 = 5, offset 20, address 2020, value 6. For grid[1][0]: linear index 3, address 2012, value 9. Multiplying by the column count skips a complete row.

A compatible parameter keeps the later dimension: void print(const int grid[][3], std::size_t rows).

A 2-by-3 grid flattened in row-major order to addresses 2000 to 2020, with grid[1][2] at address 2020 holding value 6.

Arrays at function boundaries, then safer abstractions

In void print(const int values[], std::size_t n), values becomes a pointer. sizeof(values) cannot recover six elements, so pass 6 and use i < n. Functions in C refreshes parameters; modern C++ offers safer abstractions.

int raw[6] = {14, 9, 21, 6, 18, 11};
std::array<int, 6> fixed{14, 9, 21, 6, 18, 11};
std::vector<int> dynamic{14, 9, 21, 6, 18, 11};
dynamic.push_back(25); // now seven logical elements

Use std::array for fixed compile-time size, std::vector for runtime resizing, and raw arrays for low-level interfaces, exercises, or existing APIs. On the containers, .at(4) checks bounds; operator[] does not.

Common array mistakes and precise repairs

Mistake

What goes wrong

Repair

Loop with i <= n

Touches invalid index n

Use i < n

Read automatic int data[6] before initialisation

Reads elements before valid values are written

Initialise or assign first

Assume every int is four bytes

Address calculation may be wrong

Use sizeof(int)

Use sizeof(values) / sizeof(values[0]) on a function parameter

The array has decayed to a pointer

Pass size or use a sized abstraction

Insert when n == capacity

No free slot exists

Check capacity first

Shift left-to-right during insertion

Unread values get overwritten

Shift right from the end

Confuse rows with columns in the 2D formula

Produces the wrong linear index

Multiply row by column count

For std::cout << values[6], index 6 is outside the original array and behaviour is undefined. No output or error is guaranteed.

Record capacity and logical size separately, mark 0..n-1, check bounds, use element size in address calculations, shift safely, and test the first, middle, and last valid index.

Checkable array practice

Use the exercises below to check traces, off-by-one access, address calculation, row-major flattening, and shift counts independently.

  1. Under the earlier address assumptions, &values[5] = 1000 + 5 * 4 = 1020; the value is 11.

  2. After values[3] = 16, the sum is 14 + 9 + 21 + 16 + 18 + 11 = 89.

  3. To print the original array backwards without unsigned-index trouble, use for (std::size_t i = 6; i > 0; --i) std::cout << values[i - 1] << ' ';. The output is 11 18 6 21 9 14.

  4. For grid[1][1], the linear index is 1 * 3 + 1 = 4; its address is 2000 + 4 * 4 = 2016, and its value is 2.

The short version

Arrays keep same-type elements contiguously at indices 0..n-1. Address calculations need a base, dimensions, and element size. Insertion or deletion may shift elements; direct indexing does not. Row-major storage flattens rows in order.

For a structured next step, continue with the C++ Programming course. For broader placement-oriented coding practice, use the Coding for Placements course.