C Programming: Low-Level Mastery
HomeInsightsCoursesC ProgrammingMultidimensional Arrays
Arrays

Multidimensional Arrays

Master 2D and 3D arrays for matrices, tables, and grids. Learn row-major order, advanced indexing, passing to functions, and practical applications of multidimensional data structures.

Understanding Multidimensional Arrays

Multidimensional arrays are arrays of arrays. A 2D array is an array of 1D arrays, a 3D array is an array of 2D arrays, and so on. They're perfect for representing matrices, tables, game boards, images, and any data naturally organized in multiple dimensions. Despite appearing as separate dimensions, they're stored linearly in memory in row-major order.

C
#include <stdio.h>

int main(void) {
    /* 2D array: 3 rows, 4 columns */
    int matrix[3][4] = {
        {1,  2,  3,  4},
        {5,  6,  7,  8},
        {9, 10, 11, 12}
    };
    
    /* Access elements */
    printf("%d\n", matrix[0][0]);  // 1 (row 0, col 0)
    printf("%d\n", matrix[1][2]);  // 7 (row 1, col 2)
    printf("%d\n", matrix[2][3]);  // 12 (row 2, col 3)
    
    /* Iterate through 2D array */
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%2d ", matrix[i][j]);
        }
        printf("\n");
    }
    
    return 0;
}

/* Memory layout (row-major order):
   [1][2][3][4][5][6][7][8][9][10][11][12]
   Row 0----  Row 1----  Row 2------
*/

2D Array Operations

Two-dimensional arrays represent matrices, tables, and grids. Common operations include initialization, traversal, transposition, and matrix arithmetic. Understanding these patterns is essential for scientific computing, image processing, and game development.

C
/* Initialize 2D array */
void init_matrix(int rows, int cols, int matrix[rows][cols], int value) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = value;
        }
    }
}

/* Print 2D array */
void print_matrix(int rows, int cols, int matrix[rows][cols]) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%4d ", matrix[i][j]);
        }
        printf("\n");
    }
}

/* Matrix addition */
void add_matrices(int rows, int cols, 
                  int a[rows][cols], 
                  int b[rows][cols], 
                  int result[rows][cols]) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            result[i][j] = a[i][j] + b[i][j];
        }
    }
}

/* Matrix multiplication */
void multiply_matrices(int m, int n, int p,
                      int a[m][n],
                      int b[n][p],
                      int result[m][p]) {
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < p; j++) {
            result[i][j] = 0;
            for (int k = 0; k < n; k++) {
                result[i][j] += a[i][k] * b[k][j];
            }
        }
    }
}

/* Transpose matrix */
void transpose(int rows, int cols,
               int src[rows][cols],
               int dest[cols][rows]) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            dest[j][i] = src[i][j];
        }
    }
}

/* Find max in 2D array */
int find_max_2d(int rows, int cols, int arr[rows][cols]) {
    int max = arr[0][0];
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (arr[i][j] &gt; max) {
                max = arr[i][j];
            }
        }
    }
    return max;
}

/* Sum of all elements */
int sum_2d(int rows, int cols, int arr[rows][cols]) {
    int sum = 0;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            sum += arr[i][j];
        }
    }
    return sum;
}

Practical Applications

Multidimensional arrays have countless real-world applications. From game boards to image processing, understanding how to work with these structures is essential for many programming domains.

C
/* Tic-Tac-Toe board */
char board[3][3] = {
    {' ', ' ', ' '},
    {' ', ' ', ' '},
    {' ', ' ', ' '}
};

void display_board(char board[3][3]) {
    for (int i = 0; i < 3; i++) {
        printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
        if (i < 2) printf("---|---|---\n");
    }
}

int check_winner(char board[3][3]) {
    /* Check rows */
    for (int i = 0; i < 3; i++) {
        if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ') {
            return 1;  // Winner found
        }
    }
    
    /* Check columns */
    for (int j = 0; j < 3; j++) {
        if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ') {
            return 1;
        }
    }
    
    /* Check diagonals */
    if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') {
        return 1;
    }
    if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ') {
        return 1;
    }
    
    return 0;  // No winner
}

/* Image representation (grayscale) */
#define WIDTH 640
#define HEIGHT 480

unsigned char image[HEIGHT][WIDTH];  // Each pixel 0-255

void invert_image(void) {
    for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
            image[y][x] = 255 - image[y][x];
        }
    }
}

/* Distance matrix */
#define CITIES 5
int distances[CITIES][CITIES] = {
    {0,  10, 15, 20, 25},
    {10, 0,  35, 25, 30},
    {15, 35, 0,  30, 20},
    {20, 25, 30, 0,  15},
    {25, 30, 20, 15, 0}
};

int shortest_distance(int from, int to) {
    return distances[from][to];
}

/* Seating chart */
#define ROWS 10
#define COLS 8

int seats[ROWS][COLS] = {0};  // 0 = available, 1 = taken

int book_seat(int row, int col) {
    if (row < 0 || row >= ROWS || col < 0 || col >= COLS) {
        return 0;  // Invalid
    }
    if (seats[row][col] == 1) {
        return 0;  // Already taken
    }
    seats[row][col] = 1;
    return 1;  // Success
}

3D Arrays and Beyond

Three-dimensional arrays extend the concept further, useful for voxel data, 3D games, video frames, and scientific simulations. While less common than 2D arrays, they follow the same principles with an additional dimension.

C
/* 3D array: layers × rows × columns */
int cube[2][3][4] = {
    {  /* Layer 0 */
        {1,  2,  3,  4},
        {5,  6,  7,  8},
        {9, 10, 11, 12}
    },
    {  /* Layer 1 */
        {13, 14, 15, 16},
        {17, 18, 19, 20},
        {21, 22, 23, 24}
    }
};

/* Access 3D element */
printf("%d\n", cube[0][1][2]);  // 7 (layer 0, row 1, col 2)
printf("%d\n", cube[1][2][3]);  // 24 (layer 1, row 2, col 3)

/* Iterate 3D array */
void print_cube(int layers, int rows, int cols, 
                int arr[layers][rows][cols]) {
    for (int l = 0; l < layers; l++) {
        printf("Layer %d:\n", l);
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                printf("%3d ", arr[l][i][j]);
            }
            printf("\n");
        }
        printf("\n");
    }
}

/* RGB image (3 color channels) */
#define WIDTH 640
#define HEIGHT 480
unsigned char rgb_image[HEIGHT][WIDTH][3];  // [y][x][channel]

void set_pixel(int x, int y, unsigned char r, unsigned char g, unsigned char b) {
    if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) {
        rgb_image[y][x][0] = r;
        rgb_image[y][x][1] = g;
        rgb_image[y][x][2] = b;
    }
}

/* Video frames (time dimension) */
#define FRAMES 30
#define HEIGHT 480
#define WIDTH 640
unsigned char video[FRAMES][HEIGHT][WIDTH];  // 30 frames

/* 3D game world (voxels) */
#define WORLD_X 100
#define WORLD_Y 100
#define WORLD_Z 50
int voxels[WORLD_Z][WORLD_Y][WORLD_X];  // z,y,x coordinates

int get_block(int x, int y, int z) {
    if (x >= 0 && x < WORLD_X && 
        y >= 0 && y < WORLD_Y && 
        z >= 0 && z < WORLD_Z) {
        return voxels[z][y][x];
    }
    return 0;  // Air/empty
}

void set_block(int x, int y, int z, int block_type) {
    if (x >= 0 && x < WORLD_X && 
        y >= 0 && y < WORLD_Y && 
        z >= 0 && z < WORLD_Z) {
        voxels[z][y][x] = block_type;
    }
}

Passing Multidimensional Arrays to Functions

Passing multidimensional arrays to functions requires specifying all dimensions except the first. This is because C needs to know how to calculate the memory offset for element access. Variable length arrays (VLAs) in C99+ allow more flexibility.

C
/* Traditional: All dimensions except first must be specified */
void process_matrix(int rows, int matrix[][4]) {
    // Must know column size (4) at compile time
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            matrix[i][j] *= 2;
        }
    }
}

/* C99 VLA: Dynamic dimensions */
void process_matrix_vla(int rows, int cols, int matrix[rows][cols]) {
    // Dimensions passed as parameters
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] *= 2;
        }
    }
}

/* Alternative: Pass as 1D array, calculate indices manually */
void process_as_1d(int rows, int cols, int *matrix) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i * cols + j] *= 2;  // Manual offset calculation
        }
    }
}

/* Usage */
int main(void) {
    int mat1[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
    
    /* Traditional */
    process_matrix(3, mat1);
    
    /* VLA (C99+) */
    process_matrix_vla(3, 4, mat1);
    
    /* As 1D pointer */
    process_as_1d(3, 4, (int*)mat1);
    
    return 0;
}

/* 3D array to function */
void process_3d(int layers, int rows, int cols,
                int arr[layers][rows][cols]) {
    for (int l = 0; l < layers; l++) {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                arr[l][i][j] += 10;
            }
        }
    }
}

/* Const multidimensional array */
int sum_matrix_const(int rows, int cols, 
                     const int matrix[rows][cols]) {
    int sum = 0;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            sum += matrix[i][j];
        }
    }
    return sum;
}

Dynamic Allocation of Multidimensional Arrays

Static multidimensional arrays have fixed dimensions at compile time. For runtime-determined sizes, you need dynamic allocation. Several approaches exist, each with trade-offs for memory layout and access.

C
#include <stdlib.h>

/* Method 1: Allocate as 1D array, index manually */
int* create_matrix_1d(int rows, int cols) {
    int *matrix = malloc(rows * cols * sizeof(int));
    return matrix;
}

int get_element(int *matrix, int rows, int cols, int i, int j) {
    return matrix[i * cols + j];  // Manual indexing
}

void set_element(int *matrix, int rows, int cols, int i, int j, int value) {
    matrix[i * cols + j] = value;
}

/* Method 2: Array of pointers (non-contiguous rows) */
int** create_matrix_2d(int rows, int cols) {
    int **matrix = malloc(rows * sizeof(int*));
    for (int i = 0; i < rows; i++) {
        matrix[i] = malloc(cols * sizeof(int));
    }
    return matrix;
}

void free_matrix_2d(int **matrix, int rows) {
    for (int i = 0; i < rows; i++) {
        free(matrix[i]);
    }
    free(matrix);
}

/* Method 3: Contiguous allocation with pointer array */
int** create_matrix_contiguous(int rows, int cols) {
    int **matrix = malloc(rows * sizeof(int*));
    int *data = malloc(rows * cols * sizeof(int));
    
    for (int i = 0; i < rows; i++) {
        matrix[i] = data + i * cols;
    }
    
    return matrix;
}

void free_matrix_contiguous(int **matrix) {
    free(matrix[0]);  // Free data block
    free(matrix);     // Free pointer array
}

/* Usage comparison */
void dynamic_example(void) {
    int rows = 100, cols = 50;
    
    /* Method 1: Manual indexing */
    int *mat1 = create_matrix_1d(rows, cols);
    set_element(mat1, rows, cols, 10, 20, 42);
    int val = get_element(mat1, rows, cols, 10, 20);
    free(mat1);
    
    /* Method 2: Array syntax */
    int **mat2 = create_matrix_2d(rows, cols);
    mat2[10][20] = 42;  // Natural syntax
    val = mat2[10][20];
    free_matrix_2d(mat2, rows);
    
    /* Method 3: Array syntax + contiguous memory */
    int **mat3 = create_matrix_contiguous(rows, cols);
    mat3[10][20] = 42;  // Natural syntax + cache friendly
    val = mat3[10][20];
    free_matrix_contiguous(mat3);
}

Summary & What's Next

Key Takeaways:

  • ✅ Multidimensional arrays are arrays of arrays
  • ✅ Stored in row-major order in memory
  • ✅ 2D arrays perfect for matrices, tables, boards
  • ✅ Must specify all dimensions except first in function parameters
  • ✅ C99 VLAs allow runtime-determined dimensions
  • ✅ Can allocate dynamically for runtime sizes
  • ✅ 3D+ arrays extend naturally for volumetric data
  • ✅ Practical for games, images, simulations

What's Next?

Let's learn about array algorithms - searching and sorting!