← Back to C Course | Chapter 12: Advanced Topics | Lesson 8 of 20

C Multidimensional Pointer

Pointers to Pointers

A pointer to a pointer stores the address of another pointer variable rather than the address of ordinary data, and is declared in C with two asterisks, like int **pp;. This extra layer of indirection is what lets you modify a pointer's own value (not just the data it points to) from inside another function.

Example: Pointers to Pointers

c
#include <stdio.h>
int main() {
	int x = 5;
	int *p = &x;
	int **pp = &p;
	printf("%d", **pp);
	return 0;
}

Pointer to an Array

A pointer to an array can hold the address of an entire row of a 2D array at once, rather than pointing at a single element, which lets you pass whole rows into functions without copying their contents. Declaring the parameter type correctly (matching the row's fixed inner dimension) is what makes this kind of pointer arithmetic safe.

Example: Pointer to an Array

c
#include <stdio.h>
int main() {
	int grid[2][3] = {{1,2,3},{4,5,6}};
	int (*rowPtr)[3] = grid;
	printf("%d", rowPtr[1][2]);
	return 0;
}

Dynamically Allocating 2D Arrays

To build a 2D array dynamically, first allocate an array of pointers to represent each row, then allocate a separate block of memory for each individual row and store its address in the corresponding pointer slot. This two-step allocation is what lets each row have a size decided at runtime, unlike a fixed-size 2D array.

Example: Dynamically Allocating 2D Arrays

c
#include <stdio.h>
#include <stdlib.h>
int main() {
	int rows = 2, cols = 2;
	int **arr = malloc(rows * sizeof(int*));
	for (int i = 0; i < rows; i++) {
		arr[i] = malloc(cols * sizeof(int));
	}
	arr[0][0] = 5;
	printf("%d", arr[0][0]);
	for (int i = 0; i < rows; i++) free(arr[i]);
	free(arr);
	return 0;
}

Accessing 2D Array with Pointers

Because a 2D array's elements sit contiguously in memory, the expression *(*(arr + i) + j) computes the exact same address as the more familiar arr[i][j] — both walk to row i, then j elements further into that row. Understanding this equivalence is what makes raw pointer traversal of matrices possible.

Example: Accessing 2D Array with Pointers

c
#include <stdio.h>
int main() {
	int arr[2][2] = {{1,2},{3,4}};
	printf("%d", *(*(arr + 1) + 0));
	return 0;
}

Freeing Dynamic 2D Arrays

When freeing a dynamically allocated 2D array, you must free each individually allocated row before freeing the array of row pointers itself, and doing it in the opposite order leaves the row memory permanently leaked since you'd lose the only pointers that referenced it. Always free from the inside out.

Example: Freeing Dynamic 2D Arrays

c
#include <stdio.h>
#include <stdlib.h>
int main() {
	int rows = 2;
	int **arr = malloc(rows * sizeof(int*));
	for (int i = 0; i < rows; i++) {
		arr[i] = malloc(2 * sizeof(int));
	}
	for (int i = 0; i < rows; i++) free(arr[i]);
	free(arr);
	printf("Freed rows first, then the row array");
	return 0;
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.