CppMultidimensional Arrays

Multidimensional Arrays

A multidimensional array is an array of arrays. The most common case is a 2D array, which is useful for representing grids, matrices, tables, and game boards. C++ supports arrays with any number of dimensions, but two dimensions covers the vast majority of real use cases.

Declaring and initializing a 2D array

A 2D array declaration lists two sizes: the number of rows and the number of columns. You can initialize it with nested braces, one inner list per row.

grid_basics.cpp

CPP
#include <iostream>

int main() {
    int grid[3][4];   // 3 rows, 4 columns — uninitialized

    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    // Accessing a single element: row 1, column 2 -> 7
    std::cout << matrix[1][2] << std::endl;

    // Modifying an element
    matrix[0][0] = 100;

    return 0;
}
How 2D arrays are stored in memory
C++ has no notion of a “true” 2D block of memory shaped like a grid — under the hood, a 2D array is just one long, contiguous, one-dimensional block of memory. The compiler lays rows out one after another; this is called row-major order. For matrix[3][4], all 4 elements of row 0 come first, then all 4 elements of row 1, then row 2. The expression matrix[i][j] is really syntactic sugar for indexing into that flat block at position i * numColumns + j.
Traversing with nested loops

grid_traverse.cpp

CPP
#include <iostream>

int main() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    for (int row = 0; row < 3; row++) {
        for (int col = 0; col < 4; col++) {
            std::cout << matrix[row][col] << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}
Passing multidimensional arrays to functions

Just like 1D arrays, a 2D array decays to a pointer when passed to a function — but the syntax has a quirk: the compiler needs to know the size of every dimension except the first, because it uses those sizes to compute the memory offset for each row.

pass_2d_array.cpp

CPP
#include <iostream>

// The column count (4) MUST be specified; the row count can be omitted.
void printMatrix(int m[][4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            std::cout << m[i][j] << " ";
        }
        std::cout << std::endl;
    }
}

int main() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    printMatrix(matrix, 3);

    return 0;
}
Why all but the first dimension is required
The compiler needs the column count to calculate how many bytes to skip to get from one row to the next (row * columns + col). Without it, m[i][j] would be impossible to compute. This requirement gets more awkward with 3D+ arrays, which is one reason many C++ programmers reach for std::vector or a flattened 1D array with manual indexing instead.
std::vector<std::vector<int>> — a flexible alternative

When the size of your grid isn't known until runtime, or needs to change, a vector of vectors is a common and much more flexible alternative. Each inner vector is its own independently-sized, heap-allocated array, so rows don't even have to be the same length.

vector_of_vectors.cpp

CPP
#include <iostream>
#include <vector>

int main() {
    int rows = 3;
    int cols = 4;

    // Creates a 3x4 grid, every element initialized to 0
    std::vector<std::vector<int>> grid(rows, std::vector<int>(cols, 0));

    grid[1][2] = 42;

    for (const auto& row : grid) {
        for (int value : row) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }

    std::cout << "Rows: " << grid.size() << std::endl;
    std::cout << "Columns: " << grid[0].size() << std::endl;

    return 0;
}

Feature

int grid[3][4]

vector<vector<int>>

Size fixed at compile time

Yes

No — resizable at runtime

Memory layout

One contiguous block

Separate heap block per row

Rows can differ in length

No

Yes (jagged arrays)

Passing to functions

Awkward syntax

Simple, pass by reference

Typical use case

Small, fixed-size grids

Dynamic or large data sets