C Multidimensional Arrays: 2D Arrays, Matrices & Grids (2026)
Table of Contents
What Are Multidimensional Arrays?
A multidimensional array is an array of arrays. The most common type is the 2D array, which you can think of as a table with rows and columns. If you have completed our lesson on C Arrays, you already understand one-dimensional arrays. A 2D array extends that concept into a grid structure.
2D arrays are used everywhere: spreadsheet data, game boards (chess, tic-tac-toe), image pixels, mathematical matrices, and adjacency matrices in graph algorithms. Understanding them is essential for any serious C programmer.
Declaring and Initializing 2D Arrays
The syntax for a 2D array is: type name[rows][cols];
// 3 rows, 4 columns — 12 elements total
int matrix[3][4];
// Initialize with values
int grid[3][4] = {
{1, 2, 3, 4}, // row 0
{5, 6, 7, 8}, // row 1
{9, 10, 11, 12} // row 2
};
// Flat initialization (fills row by row)
int flat[2][3] = {1, 2, 3, 4, 5, 6};
// Same as: {{1,2,3}, {4,5,6}}
// Partial initialization (rest becomes 0)
int sparse[3][3] = {
{1}, // {1, 0, 0}
{0, 5}, // {0, 5, 0}
{0, 0, 9} // {0, 0, 9}
};
// Zero-initialize entire 2D array
int zeros[10][10] = {0};
When you provide initial values, the compiler can infer the number of rows (but not columns):
int auto_rows[][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// Compiler deduces: 3 rows
Accessing 2D Array Elements
int grid[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
printf("%d\n", grid[0][0]); // 1 (first row, first col)
printf("%d\n", grid[1][2]); // 7 (second row, third col)
printf("%d\n", grid[2][3]); // 12 (last row, last col)
grid[1][1] = 99; // modify element
The first index selects the row, the second selects the column. Both are zero-based, just like regular arrays.
Looping Through 2D Arrays
Use nested C for Loop — the outer loop iterates rows, the inner loop iterates columns:
#include <stdio.h>
int main(void) {
int grid[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Print as a formatted table
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 4; c++) {
printf("%4d", grid[r][c]);
}
printf("\n");
}
return 0;
}
Output:
1 2 3 4
5 6 7 8
9 10 11 12
Sum All Elements
int sum = 0;
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 4; c++) {
sum += grid[r][c];
}
}
printf("Total: %d\n", sum); // 78
Row Sums and Column Sums
// Row sums
for (int r = 0; r < 3; r++) {
int row_sum = 0;
for (int c = 0; c < 4; c++) {
row_sum += grid[r][c];
}
printf("Row %d sum: %d\n", r, row_sum);
}
// Column sums
for (int c = 0; c < 4; c++) {
int col_sum = 0;
for (int r = 0; r < 3; r++) {
col_sum += grid[r][c];
}
printf("Col %d sum: %d\n", c, col_sum);
}
Memory Layout: Row-Major Order
C stores 2D arrays in row-major order — all elements of row 0 come first in memory, then all of row 1, and so on:
int grid[2][3] = {{1,2,3}, {4,5,6}};
// Memory layout:
// [1][2][3][4][5][6]
// row 0 row 1
// grid[r][c] is at memory offset: r * cols + c
// grid[1][2] = offset 1*3 + 2 = 5 → value 6
This matters for performance. Accessing elements row-by-row (varying the column index in the inner loop) is cache-friendly because you are reading consecutive memory addresses. Accessing column-by-column (varying the row index in the inner loop) causes cache misses because you are jumping across rows.
// FAST — row-major traversal (cache-friendly)
for (int r = 0; r < ROWS; r++)
for (int c = 0; c < COLS; c++)
process(grid[r][c]);
// SLOW — column-major traversal (cache-unfriendly)
for (int c = 0; c < COLS; c++)
for (int r = 0; r < ROWS; r++)
process(grid[r][c]);
For small arrays this difference is negligible, but for large matrices (thousands of rows), the cache-friendly version can be 10x faster.
Passing 2D Arrays to Functions
When passing 2D arrays to C Functions, you must specify the number of columns:
// Column count (4) must be specified
void print_matrix(int matrix[][4], int rows) {
for (int r = 0; r < rows; r++) {
for (int c = 0; c < 4; c++) {
printf("%4d", matrix[r][c]);
}
printf("\n");
}
}
int main(void) {
int m[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
print_matrix(m, 3);
return 0;
}
The compiler needs the column count to calculate memory offsets. The row count can be variable, but columns must be a compile-time constant. This is one of the limitations of C’s array system.
Using Pointers for Flexible Sizes (C99+)
// VLA syntax allows both dimensions to be parameters
void print_any(int rows, int cols, int matrix[rows][cols]) {
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
printf("%4d", matrix[r][c]);
}
printf("\n");
}
}
This C99 VLA parameter syntax is the cleanest way to handle variable-sized 2D arrays. Note that rows and cols must appear before the array parameter in the function signature.
Matrix Operations
Matrix Addition
#define N 3
void matrix_add(int a[N][N], int b[N][N], int result[N][N]) {
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
result[r][c] = a[r][c] + b[r][c];
}
}
}
Matrix Multiplication
void matrix_multiply(int a[N][N], int b[N][N], int result[N][N]) {
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
result[r][c] = 0;
for (int k = 0; k < N; k++) {
result[r][c] += a[r][k] * b[k][c];
}
}
}
}
Matrix Transpose
void transpose(int src[N][N], int dst[N][N]) {
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
dst[c][r] = src[r][c];
}
}
}
3D Arrays and Beyond
C supports arrays with any number of dimensions:
int cube[3][4][5]; // 3D: 3 layers, 4 rows, 5 columns = 60 elements
cube[0][1][2] = 42;
// Triple nested loop
for (int l = 0; l < 3; l++)
for (int r = 0; r < 4; r++)
for (int c = 0; c < 5; c++)
cube[l][r][c] = l * 20 + r * 5 + c;
3D arrays are used for RGB image data (height × width × channels), volumetric data, and time-series matrices. Beyond 3 dimensions, consider using flattened 1D arrays with manual index calculations for better readability.
Practical Examples
Tic-Tac-Toe Board
#include <stdio.h>
void print_board(char board[3][3]) {
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {
printf(" %c ", board[r][c]);
if (c < 2) printf("|");
}
printf("\n");
if (r < 2) printf("---+---+---\n");
}
}
int main(void) {
char board[3][3] = {
{'X', 'O', 'X'},
{' ', 'X', 'O'},
{'O', ' ', 'X'}
};
print_board(board);
return 0;
}
Grade Tracker
#include <stdio.h>
#define STUDENTS 4
#define SUBJECTS 3
int main(void) {
int grades[STUDENTS][SUBJECTS] = {
{85, 90, 78},
{92, 88, 95},
{70, 75, 80},
{88, 82, 91}
};
const char *names[] = {"Alice", "Bob", "Carol", "Dave"};
const char *subjects[] = {"Math", "Science", "English"};
// Print grade table
printf("%-8s", "Name");
for (int s = 0; s < SUBJECTS; s++)
printf("%-10s", subjects[s]);
printf("Average\n");
for (int i = 0; i < STUDENTS; i++) {
printf("%-8s", names[i]);
int sum = 0;
for (int j = 0; j < SUBJECTS; j++) {
printf("%-10d", grades[i][j]);
sum += grades[i][j];
}
printf("%.1f\n", (double)sum / SUBJECTS);
}
return 0;
}
Common Mistakes
Mistake 1: Swapping Row and Column Indices
int grid[3][5];
// grid[5][3] — WRONG order, out of bounds on both!
// Always: grid[row][col]
Mistake 2: Omitting Column Size in Function Parameters
// void func(int arr[][]); // ERROR — column count required
void func(int arr[][4], int rows); // Correct
Mistake 3: sizeof on 2D Array Parameters
void func(int arr[][4]) {
// sizeof(arr) gives pointer size, NOT array size
// Must pass dimensions as separate parameters
}
Practice Exercises
- Identity Matrix: Write a function that creates an N×N identity matrix (1s on the diagonal, 0s elsewhere).
- Spiral Print: Print a 2D array in spiral order (outer ring first, then inner rings).
- Saddle Point: Find all saddle points in a matrix (elements that are the minimum in their row and maximum in their column).
- Magic Square: Check if a given N×N matrix is a magic square (all rows, columns, and diagonals sum to the same value).
Summary
Multidimensional arrays extend C’s array system into grids, tables, and higher-dimensional structures. You now understand row-major memory layout, cache-friendly traversal patterns, how to pass 2D arrays to functions, and core matrix operations. These concepts are the backbone of scientific computing, game development, and image processing in C.
Next up, we tackle C strings — because in C, a string is really just a character array with a special twist.