Ch5.9: Multi-dimensional C-style Arrays
Overview
A multi-dimensional C-style array is simply an array whose elements are themselves C-style arrays. The most common form is the two-dimensional array, which you can think of as a table of rows and columns.
In this chapter, you will learn:
- how to declare and initialize multi-dimensional C-style arrays
- how they are laid out in memory (row-major order)
- how indexing works
- how decay works for multi-dimensional arrays
- why only the outermost dimension decays
- what pointer types are involved (e.g.,
int (*)[N])
1. Declaring a multi-dimensional C-style array
A two-dimensional array is declared by specifying both dimensions:
int a[2][3]{
{1, 2, 3},
{4, 5, 6}
};
This means:
- 2 rows
- 3 columns per row
- each row is an array of 3
int
2. Memory layout: row-major order
C++ stores multi-dimensional arrays in row-major order. This means the entire first row is stored first, then the second row, and so on.
int a[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
Memory (addresses vary):
Address: 1000 1004 1008 1012 1016 1020
Memory: [ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ]
↑ ↑ ↑ ↑ ↑ ↑
a[0][0] ... ... a[1][2]
Even though the array looks like a grid, it is stored as one contiguous block.
3. Indexing multi-dimensional arrays
You access elements using multiple [] operators:
int x = a[0][1]; // 2
int y = a[1][2]; // 6
The expression a[i] refers to the i-th row, which is
itself an array of 3 int.
int b[3] = a[0]; // ❌ error: cannot copy arrays
But you can take its address:
int (*row0)[3] = ::std::addressof(a[0]);
The type of a[0] is int[3].
The type of &a[0] is int (*)[3].
4. Decay of multi-dimensional arrays
Just like one-dimensional arrays, multi-dimensional arrays decay in most expressions — but only the outermost dimension decays.
int a[2][3];
int (*p)[3] = a; // decay: a → pointer to array of 3 int
Important:
adecays toint (*)[3]a[0]does not decay until used in an expressiona[0]is an array of 3int
This is why:
int *p0 = a[0]; // decay: a[0] → &a[0][0]
But:
int (*p1)[3] = a; // pointer to row
5. Pointer arithmetic with multi-dimensional arrays
Pointer arithmetic on int (*)[3] moves by entire rows.
int (*p)[3] = a;
int (*p_next)[3] = p + 1; // moves to a[1]
Meanwhile, pointer arithmetic on int* moves by individual elements.
int *q = a[0]; // points to a[0][0]
int *q2 = q + 4; // points to a[1][1]
This works because the entire 2×3 array is contiguous.
6. Using ::std::ranges::begin and ::std::ranges::end
You can obtain iterators for the outer dimension:
#include
int a[2][3]{{1,2,3},{4,5,6}};
int (*first)[3] = ::std::ranges::begin(a);
int (*last)[3] = ::std::ranges::end(a);
These compute:
&a[0]forbegin(a)&a[0] + 2forend(a)
They do not search for '\0' because this is not a string.
7. Higher-dimensional arrays
You can extend this to any number of dimensions:
int a[2][3][4];
This is an array of 2 elements,
each of which is an array of 3 elements,
each of which is an array of 4 int.
The memory is still one contiguous block.
Key takeaways
- A multi-dimensional C-style array is an array of arrays.
- C++ stores multi-dimensional arrays in row-major order.
a[i]is itself an array.&a[i]has typeint (*)[N]for the inner dimension.- Only the outermost dimension decays.
adecays toint (*)[N].a[i]decays toint*when used in expressions.- Pointer arithmetic on
int (*)[N]moves by rows. - Pointer arithmetic on
int*moves by individual elements. - Use
::std::ranges::begin(a)and::std::ranges::end(a)for the outer dimension.