CMultidimensional Arrays

Multidimensional Arrays

C supports arrays with more than one dimension, most commonly two-dimensional arrays used to represent grids, matrices, or tables of data. A multidimensional array is really just an array of arrays, and understanding how it is laid out in memory makes several tricky C rules — especially around function parameters — much clearer.

Declaring a 2D Array

A two-dimensional array is declared with two size specifiers: the number of rows and the number of columns.

C
int grid[3][4]; // 3 rows, 4 columns -- 12 ints total

int matrix[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};
Row-Major Storage Order
Even though you write grid[3][4] with two indices, the underlying memory is a single, flat, contiguous block. C stores multidimensional arrays in row-major order: the entire first row is stored first, followed immediately by the entire second row, and so on. For grid[3][4], the layout in memory looks like:

C
// grid[3][4] in memory (row-major):
// [row0col0][row0col1][row0col2][row0col3][row1col0][row1col1]...[row2col3]
//
// grid[i][j] lives at flat offset: i * 4 + j
Why this matters
Knowing the layout is row-major explains why looping with the row index as the outer loop and the column index as the inner loop is generally more cache-friendly — it walks through memory sequentially instead of jumping around.
Accessing Elements and Nested Traversal
You access an element with two subscripts, grid[i][j], where i is the row and j is the column. Visiting every element typically means a nested loop: an outer loop over rows and an inner loop over columns.

C
#include <stdio.h>

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

    for (int i = 0; i < 3; i++) {       // rows
        for (int j = 0; j < 4; j++) {   // columns
            printf("%3d", grid[i][j]);
        }
        printf("\n");
    }
    return 0;
}
Passing Multidimensional Arrays to Functions
All dimensions except the first must be specified
When a multidimensional array decays into a pointer for a function parameter, the compiler needs to know the size of every dimension except the first in order to compute the correct memory offset for arr[i][j]. This produces a genuine C syntax quirk: the column count (and any deeper dimension) is required, but the row count may be omitted.

C
#include <stdio.h>

// The "4" here is REQUIRED -- the compiler needs it to compute offsets.
// The row count is not required and can be left empty, or passed separately.
void printGrid(int arr[][4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%3d", arr[i][j]);
        }
        printf("\n");
    }
}

/* void printGridBad(int arr[][], int rows) { ... } */ // ERROR: won't compile,
                                                          // column size is missing

int main(void) {
    int grid[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
    printGrid(grid, 3);
    return 0;
}
  • A 2D array int grid[3][4] has 3 rows and 4 columns, stored as one contiguous block

  • C stores multidimensional arrays in row-major order: entire rows are laid out one after another

  • grid[i][j] maps to the flat memory offset i * numColumns + j

  • Traversal is usually a nested loop: outer loop over rows, inner loop over columns

  • When passed to a function, every dimension except the first must be specified, e.g. void f(int arr[][4])