Using Pointers with Multidimensional Arrays in C

Explore how pointers interact with multidimensional arrays in C programming. This chapter covers the basics of arrays, pointers, and how to effectively manage multidimensional data structures, enhancing your understanding of memory management and data manipulation in C.



Pointers and Multidimensional Arrays in C

In C, an array is a collection of values of the same type stored in contiguous memory locations. Each element in an array, whether one-dimensional or multi-dimensional, is identified by one or more unique integer indices.

A pointer, however, stores the address of a variable. The address of the first element in an array is the pointer to that array. You can use the "dereference operator" to access the value that a pointer refers to.

You can declare one-dimensional, two-dimensional, or multidimensional arrays in C. The term "dimension" refers to the number of indices required to identify an element in a collection.

Pointers and One-dimensional Arrays

In a one-dimensional array, each element is identified by a single integer:

int a[5] = {1, 2, 3, 4, 5};

Here, the number "1" is at index 0, "2" is at index 1, and so on.

A variable that stores the address of the first element is its pointer:

int *x = &a[0];

The name of the array also points to the address of the first element, allowing you to use this expression:

int *x = a;

Example

Since the value of the pointer increments by the size of the data type, x++ moves the pointer to the next element in the array.

Syntax

#include 

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int length = sizeof(arr) / sizeof(arr[0]);
    int i = 0;

    int *ptr = arr;

    while (i < length) {
        printf("arr[%d]: %d \n", i, *(ptr + i));
        i++;
    }

    return 0;
}
        
Output

arr[0]: 1
arr[1]: 2
arr[2]: 3
arr[3]: 4
arr[4]: 5
        

Pointers and Two-dimensional Arrays

If a one-dimensional array is like a list of elements, a two-dimensional array resembles a table or a matrix.

The elements in a 2D array are logically arranged in rows and columns, and the location of any element is determined by its row and column indices, both starting from "0".

int arr[2][2];

This array can be visualized as follows:

Col0 Col1 Col2
Row0 arr[0][0] arr[0][1] arr[0][2]
Row1 arr[1][0] arr[1][1] arr[1][2]
Row2 arr[2][0] arr[2][1] arr[2][2]

Note that this tabular arrangement is merely a logical representation. The compiler allocates a block of contiguous bytes. In C, array allocation is done in a row-major order, meaning elements are stored in the array row-wise.

For example, we can declare a 2D array with three rows and four columns as follows:


int arr[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

The compiler allocates memory for this 2D array in a row-wise manner. Assuming the first element of the array is at address 1000 and the size of an int is 4 bytes, the elements will be allocated the following memory locations:

Row 0 Row 1 Row 2
Value: 1 2 3 4 5 6 7 8 9 10 11 12
Address: 1000 1004 1008 1012 1016 1020 1024 1028 1032 1036 1040 1044

To access the elements using a pointer, we assign the address of the first element of the array to the pointer ptr:

int *ptr = &arr[0][0];

Example 1

If the pointer is incremented by 1, it moves to the next address. All 12 elements in the "3×4" array can be accessed using the following loop:

Syntax

#include 

int main() {
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
    };

    // Pointer ptr pointing at array arr
    int *ptr = &arr[0][0];

    int i, j, k = 0;

    // Print the elements of the array arr via pointer ptr
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 4; j++) {
            printf("%d   ", *(ptr + k));
            k++;
        }
        printf("\n");
    }

    return 0;
}
        
Output

1   2   3   4   
5   6   7   8   
9   10   11   12
        

In general, the address of any element of the array can be calculated using the following formula:


address of element at ith row and jth col = baseAddress + [(i * number_of_columns + j) * sizeof(array_type)]

In our 3×4 array:


address of arr[2][2] = 1000 + (2 * 4 + 2) * 4 = 1044

This confirms that the address of arr[2][2] is 1044.

Example 2

We can use the dereference pointer to fetch the value at the address. Let’s use this formula to traverse the array with the help of its pointer:

Syntax

#include 

int main() {
    // 2D array
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    int ROWS = 3, COLS = 4;
    int i, j;

    // Pointer
    int *ptr = &arr[0][0];

    // Print the elements of the array via pointer ptr
    for (i = 0; i < ROWS; i++) {
        for (j = 0; j < COLS; j++) {
            printf("%4d ", *(ptr + (i * COLS + j)));
        }
        printf("\n");
    }

    return 0;
}
        
Output

   1    2    3    4
   5    6    7    8
   9   10   11   12
        

Pointers and Three-dimensional Arrays

A three-dimensional array is essentially an array of two-dimensional arrays. Such an array is declared with three subscripts:

int arr[x][y][j];

This can be viewed as "x" layers of tables, each having "y" rows and "z" columns.

For example:


int arr[3][3][3] = {
    { {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
    { {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
    { {31, 32, 33}, {34, 35, 36}, {37, 38, 39} },
};

A pointer to the 3D array can be declared as:

int *ptr = &arr[0][0][0];

Since the name of the array itself is the address of the first element, we can also write:

int *ptr = arr;

Each layer of "x" rows and "y" columns occupies:

x * y * sizeof(data_type>

Assuming the memory allocated for the 3D array "arr" starts at address 1000, the second layer (with "i = 1") begins at:

1000 + (3 * 3) * 4 = 1036

If JMAX is the number of rows and KMAX is the number of columns, the address of the element at the 0th row and 0th column of the 1st slice can be calculated as:

arr[1][0][0] = ptr + (1 * JMAX * KMAX)

The formula for accessing the value of an element at the jth row and kth column of the ith slice is:

arr[i][j][k] = *(ptr + (i * JMAX * KMAX) + (j * KMAX + k))

Example: Printing a 3D Array using Pointer Dereferencing

Let’s apply this formula to print the 3D array using pointer dereferencing:

Syntax

#include 

int main() {
    int i, j, k;
    int arr[3][3][3] = {
        { {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
        { {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
        { {31, 32, 33}, {34, 35, 36}, {37, 38, 39} },
    };

    int JMAX = 3, KMAX = 3;
    int *ptr = arr; // &arr[0][0][0];

    for(i = 0; i < 3; i++) {
        for(j = 0; j < 3; j++) {
            for(k = 0; k < 3; k++) {
                printf("%d ", *(ptr + (i * JMAX * KMAX) + (j * KMAX + k)));
            }
            printf("\n");
        }
        printf("\n");
    }

    return 0;
}
        
Output

11 12 13 
14 15 16 
17 18 19 

21 22 23 
24 25 26 
27 28 29 

31 32 33 
34 35 36 
37 38 39
        

In summary, accessing an array using a pointer is quite similar to accessing it with subscript notation. The main difference is that subscripted arrays allocate memory statically, while pointers allow for dynamic memory allocation.

To pass a multi-dimensional array to a function, you need to use pointers instead of subscripts. However, using subscripted arrays is generally more convenient for beginners compared to pointers, which can be more challenging to grasp.