C Mastery / Arrays Part 2: Multidimensional, VLA, Pointer-to-Array
Part 1 — The Core Language

Arrays Part 2: Multidimensional, VLA, Pointer-to-Array

This chapter covers multidimensional arrays, pointers to arrays, and variable-length arrays (VLAs). These are the more advanced array forms, and each has important portability and undefined-behavior implications.

Why This Matters

Multidimensional arrays are the natural way to represent matrices and grids. Pointers to arrays are required to pass true multidimensional arrays to functions. VLAs are a C99 feature that is optional in C11 and controversial — you must know when and whether to use them.

Prerequisites

Core Concept

Multidimensional arrays

A multidimensional array is an array of arrays. int m[3][4] is three arrays, each of four ints, laid out contiguously in memory.

int m[3][4];   /* 3 rows, 4 columns; 12 ints total */
m[1][2] = 5;   /* row 1, column 2 */

Memory layout is row-major: the rightmost subscript varies fastest.

Pointers to arrays

int (*p)[4] is a pointer to an array of 4 ints. This is the correct type for a pointer to a row of a [N][4] array.

int m[3][4];
int (*p)[4] = m;   /* m decays to &m[0], type int (*)[4] */

Note the parentheses: int (*p)[4] is a pointer to array; int *p[4] is an array of four pointers (c.ptr.array-of-ptr).

Variable-length arrays (C99)

A VLA is an array whose size is not an integer constant expression, evaluated at run time:

void f(int n)
{
    int a[n];   /* VLA: size determined at run time */
}

VLAs have automatic storage and their size is computed when the declaration is reached. VLAs are optional in C11 (an implementation may or may not support them) and were made mandatory again in C23 in a different form. STANDARD-VERSION-DEPENDENT

Syntax

Multidimensional arrays

int m[3][4];
int m[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
int m[][4] = { {1,2,3,4}, {5,6,7,8} };   /* first dimension inferred */

Pointer to array

int (*p)[4];

VLA

int a[n];          /* VLA (automatic) */
int b[n][m];       /* VLA with multiple dimensions */

Examples

Passing a true multidimensional array

#include <stdio.h>

void print_matrix(int rows, int cols, int m[rows][cols])
{
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++)
            printf("%d ", m[i][j]);
        printf("\n");
    }
}

int main(void)
{
    int m[2][3] = { {1,2,3}, {4,5,6} };
    print_matrix(2, 3, m);
    return 0;
}

Here m[rows][cols] is a VLA parameter (the dimensions are parameters), and the compiler can index it correctly.

Pointer to array vs. array of pointers

int (*p)[4];   /* pointer to array of 4 ints */
int *q[4];     /* array of 4 pointers to int */

p + 1 moves by 4 * sizeof(int) bytes; q + 1 moves by sizeof(int *) bytes.

How It Works

A multidimensional array is contiguous; m[i][j] computes the offset i * cols + j (row-major) and indexes that element. A pointer to an array carries the inner dimension in its type, so the compiler can compute the row stride correctly. A VLA's size is materialized at run time and stored implicitly so sizeof and indexing work.

Variations

Array of pointers (ragged arrays)

int *rows[3];   /* each element can point to a different-length array */

This is a "ragged" structure, distinct from a contiguous multidimensional array. Use it when rows have different lengths.

VLA typedefs

typedef int VLA[n];   /* VLA typedef (run-time size) */

Common Mistakes

same; a [N][M] decays to int (*)[M], not int **).

Undefined Behavior

violation for non-positive size in some cases).

Portability

supported by GCC and Clang but not by MSVC. STANDARD-VERSION-DEPENDENT

Under the Hood

A pointer to an array encodes the inner dimension, letting the compiler emit the correct scaled addressing for p + 1. VLA sizes are typically stored in a hidden local (or on the stack) so sizeof and cleanup work correctly.

Practical Usage

allocation instead.

Exercises

1. Write a function that takes int (*m)[4] and prints each element of a [N][4] array, and call it. 2. Explain why int ** is not interchangeable with int (*)[4]. 3. Write a program using a VLA and another using malloc for the same task; compare them. 4. Demonstrate that sizeof on a VLA is a run-time value.

Deep Challenge

Explain the memory layout of int m[2][3] (row-major), and write a function that flattens it into a 1D array using explicit index arithmetic. Then contrast this with a ragged array of pointers and explain the performance and layout trade-offs.

References

§6.7.6.3 (function declarators), §6.2.5 (array types).

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.arr.multi05
c.arr.vla05
c.arr.ptr-to-array06