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.
#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.
/* 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] > 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.
/* 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.
/* 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.
/* 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.
#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