Dynamic 2D Arrays in C
In C, when the size of a 2D array is not known at compile time (rows or columns decided at runtime), we must use **dynamic memory allocation** (malloc/calloc).
There are **several popular approaches**, each with different memory layout, performance, and ease of use.
Comparison of Different Methods
| Method | Contiguous Memory? | Access Syntax | Ease of Use | Cache Friendly | Free Complexity |
|---|---|---|---|---|---|
| Double Pointer (Array of Pointers) | No (jagged possible) | arr[i][j] | Very Easy | Poor | Need loop |
| Single Block + Pointer Arithmetic | Yes | arr[i*n + j] | Moderate | Excellent | Single free |
| Single Block + Array of Pointers | Yes | arr[i][j] | Easy | Good | Single free |
| 1D Array + Manual Mapping | Yes | arr[i*n + j] | Fastest | Excellent | Single free |
Method 1: Double Pointer (Most Common & Easiest)
C
Double pointer (array of row pointers)
int rows = 4, cols = 5;
int **arr = (int**)malloc(rows * sizeof(int*));
if(!arr) { /* error */ }
for(int i = 0; i < rows; i++) {
arr[i] = (int*)malloc(cols * sizeof(int));
if(!arr[i]) { /* error handling */ }
}
// Use like normal 2D array
arr[2][3] = 42;
// Free memory (important!)
for(int i = 0; i < rows; i++) {
free(arr[i]);
}
free(arr);
Note: Memory is **not contiguous** → poor cache performance in large matrices
Method 2: Single Contiguous Block + Pointer Arithmetic (Best Performance)
C
int rows = 4, cols = 5;
int *arr = (int*)malloc(rows * cols * sizeof(int));
if(!arr) { /* error */ }
// Access using pointer arithmetic
*(arr + i*cols + j) = 100;
// Or with macro for readability
#define ARR(i,j) arr[(i)*cols + (j)]
ARR(2,3) = 42;
// Free - only one call!
free(arr);
Note: Best cache performance, single allocation, single free → very efficient
Method 3: Single Block + Array of Row Pointers (Best of Both Worlds)
C
Contiguous memory + normal arr[i][j] syntax
int rows = 4, cols = 5;
int **arr = (int**)malloc(rows * sizeof(int*));
int *data = (int*)malloc(rows * cols * sizeof(int));
for(int i = 0; i < rows; i++) {
arr[i] = data + i * cols;
}
// Use normally
arr[1][2] = 999;
// Free only two pointers
free(data);
free(arr);
Note: Most recommended in modern code – combines clean syntax with contiguous memory
Best Practices & Recommendations (2025+)
- Always check malloc/calloc return value
- Use **Method 3** (single block + row pointers) for most cases
- Use **Method 2** when performance is critical (matrix operations, ML, graphics)
- Never forget to free all allocated memory
- Consider using a struct to encapsulate rows, cols, and data pointer
- For very large matrices → consider libraries (like OpenBLAS, custom allocators)
Codecrown