C++ course menu
C++ 2D Arrays and Grids: Multidimensional Arrays and Vectors of Vectors
Work with tables and grids in C++: 2D C-style arrays, std::array of arrays, vectors of vectors, looping over rows and columns, row and column totals, matrix operations, and storing a grid in one flat vector.
On this page
Many problems involve grids: a seating plan, a game board, a spreadsheet, an image made of pixels, a matrix in maths. In C++ you model a grid as an array of rows, where each row is itself an array.
2D C-style arrays#
#include <iostream>
int main() {
int grid[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
};
std::cout << grid[0][0] << ' ' << grid[1][2] << ' ' << grid[2][3] << '\n';
}1 7 12grid[3][4] means 3 rows of 4 columns. grid[row][col] reaches one cell, with both indexes starting at 0. The sizes must be fixed at compile time.
Looping over a grid#
Use nested loops: the outer loop walks the rows, the inner loop walks the columns of the current row:
#include <iostream>
int main() {
const int rows = 3;
const int cols = 4;
int grid[rows][cols] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
};
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < cols; ++c) {
std::cout << grid[r][c] << '\t';
}
std::cout << '\n';
}
}1 2 3 4
5 6 7 8
9 10 11 12std::array of std::array#
The modern fixed-size version nests std::array:
#include <array>
#include <iostream>
int main() {
std::array<std::array<char, 3>, 3> board = {{
{'X', 'O', 'X'},
{' ', 'X', 'O'},
{'O', ' ', 'X'},
}};
for (const auto& row : board) {
for (char cell : row) {
std::cout << '[' << cell << ']';
}
std::cout << '\n';
}
bool diagonal = board[0][0] == 'X' && board[1][1] == 'X' && board[2][2] == 'X';
std::cout << "X wins on the diagonal: " << std::boolalpha << diagonal << '\n';
}[X][O][X]
[ ][X][O]
[O][ ][X]
X wins on the diagonal: trueThe double braces {{ ... }} are needed because std::array wraps a C-style array inside a struct.
Vectors of vectors#
When the size is only known at run time, use std::vector<std::vector<T>>:
#include <iostream>
#include <vector>
int main() {
int rows = 3;
int cols = 5;
std::vector<std::vector<int>> table(rows, std::vector<int>(cols, 0));
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < cols; ++c) {
table[r][c] = (r + 1) * (c + 1);
}
}
for (const auto& row : table) {
for (int value : row) std::cout << value << ' ';
std::cout << '\n';
}
}1 2 3 4 5
2 4 6 8 10
3 6 9 12 15std::vector<std::vector<int>>(rows, std::vector<int>(cols, 0)) creates rows copies of a row with cols zeros. Each row is a separate vector, so rows can even have different lengths ("jagged" arrays), such as Pascal's triangle.
Row and column totals#
A common task is summarizing a table. Here, sales[r][c] holds sales for shop r in month c:
#include <iostream>
#include <vector>
int main() {
std::vector<std::vector<int>> sales = {
{120, 150, 110},
{90, 95, 130},
{200, 180, 210},
};
for (std::size_t r = 0; r < sales.size(); ++r) {
int row_total = 0;
for (int v : sales[r]) row_total += v;
std::cout << "Shop " << r + 1 << ": " << row_total << '\n';
}
for (std::size_t c = 0; c < sales[0].size(); ++c) {
int col_total = 0;
for (std::size_t r = 0; r < sales.size(); ++r) col_total += sales[r][c];
std::cout << "Month " << c + 1 << ": " << col_total << '\n';
}
}Shop 1: 380
Shop 2: 315
Shop 3: 590
Month 1: 410
Month 2: 425
Month 3: 450For column totals, the column loop is on the outside.
Transposing a matrix#
Transposing swaps rows and columns: element [r][c] moves to [c][r]:
#include <iostream>
#include <vector>
int main() {
std::vector<std::vector<int>> m = {{1, 2, 3}, {4, 5, 6}};
std::vector<std::vector<int>> t(m[0].size(), std::vector<int>(m.size()));
for (std::size_t r = 0; r < m.size(); ++r) {
for (std::size_t c = 0; c < m[0].size(); ++c) {
t[c][r] = m[r][c];
}
}
for (const auto& row : t) {
for (int v : row) std::cout << v << ' ';
std::cout << '\n';
}
}1 4
2 5
3 6One flat vector#
For large grids where performance matters, store all cells in one vector of rows * cols elements and compute the position yourself: cell (r, c) is at index r * cols + c. This keeps all data in a single block of memory, which is faster than many separate row vectors:
#include <iostream>
#include <vector>
int main() {
int rows = 2;
int cols = 3;
std::vector<int> cells(rows * cols, 0);
auto at = [&](int r, int c) -> int& { return cells[r * cols + c]; };
at(1, 2) = 7;
at(0, 1) = 4;
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < cols; ++c) std::cout << at(r, c) << ' ';
std::cout << '\n';
}
}0 4 0
0 0 7Common mistakes#
Practice#
Practice exercise
Given a 3×3 matrix {{2, 7, 6}, {9, 5, 1}, {4, 3, 8}}, check whether it is a magic square: every row, every column and both diagonals add up to the same number. Print the result.
Show solution
#include <iostream>
#include <vector>
int main() {
std::vector<std::vector<int>> m = {{2, 7, 6}, {9, 5, 1}, {4, 3, 8}};
int n = static_cast<int>(m.size());
int target = 0;
for (int v : m[0]) target += v;
bool magic = true;
int diag1 = 0;
int diag2 = 0;
for (int i = 0; i < n; ++i) {
int row = 0;
int col = 0;
for (int j = 0; j < n; ++j) {
row += m[i][j];
col += m[j][i];
}
if (row != target || col != target) magic = false;
diag1 += m[i][i];
diag2 += m[i][n - 1 - i];
}
if (diag1 != target || diag2 != target) magic = false;
std::cout << "Magic square: " << std::boolalpha << magic << " (sum " << target << ")\n";
}Magic square: true (sum 15)Key takeaway#
Finished this lesson?