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

C Multidimensional Pointer

Multidimensional pointers help you work with tables of data through memory addresses, like following a map to rows and columns. You can even create and free a table while the program runs.
Syntax
c
data_type **pointer_to_pointer = &pointer_name;

data_type (*row_pointer)[columns] = array_name;  // pointer to an array row

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 standard input/output (printf, scanf)
#include <stdio.h>
// Program execution starts in main()
int main() {
	// Declare x and set it to 5
	int x = 5;
	int *p = &x;
	int **pp = &p;
	// Print formatted text to the screen
	printf("%d", **pp);
	// Return 0 to signal that the program finished successfully
	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 standard input/output (printf, scanf)
#include <stdio.h>
// Program execution starts in main()
int main() {
	int grid[2][3] = {{1,2,3},{4,5,6}};
	int (*rowPtr)[3] = grid;
	// Print formatted text to the screen
	printf("%d", rowPtr[1][2]);
	// Return 0 to signal that the program finished successfully
	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 standard input/output (printf, scanf)
#include <stdio.h>
// Include general utilities (malloc, free, exit)
#include <stdlib.h>
// Program execution starts in main()
int main() {
	int rows = 2, cols = 2;
	// Allocate memory on the heap
	int **arr = malloc(rows * sizeof(int*));
	// Loop: repeat while the condition holds
	for (int i = 0; i < rows; i++) {
		// Allocate memory on the heap
		arr[i] = malloc(cols * sizeof(int));
	}
	arr[0][0] = 5;
	// Print formatted text to the screen
	printf("%d", arr[0][0]);
	// Loop: repeat while the condition holds
	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 standard input/output (printf, scanf)
#include <stdio.h>
// Program execution starts in main()
int main() {
	int arr[2][2] = {{1,2},{3,4}};
	// Print formatted text to the screen
	printf("%d", *(*(arr + 1) + 0));
	// Return 0 to signal that the program finished successfully
	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 standard input/output (printf, scanf)
#include <stdio.h>
// Include general utilities (malloc, free, exit)
#include <stdlib.h>
// Program execution starts in main()
int main() {
	// Declare rows and set it to 2
	int rows = 2;
	// Allocate memory on the heap
	int **arr = malloc(rows * sizeof(int*));
	// Loop: repeat while the condition holds
	for (int i = 0; i < rows; i++) {
		// Allocate memory on the heap
		arr[i] = malloc(2 * sizeof(int));
	}
	// Loop: repeat while the condition holds
	for (int i = 0; i < rows; i++) free(arr[i]);
	free(arr);
	printf("Freed rows first, then the row array");
	return 0;
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Mixing up a pointer to an array with an array of pointers because of the parentheses in the declaration.
  2. Freeing only the array of row pointers and not each individual row, causing a memory leak.
  3. Indexing a dynamically allocated 2D array incorrectly, such as swapping row and column.

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.