Multi-dimensional arrays

A multi-dimensional array can be seen as an “array of arrays.”

A dd-dimensional array is an array of d1d-1-dimensional arrays, in which we can use nested braces to group the different levels of elements.

The most commonly used multi-dimensional array is a 2-D (two-dimensional) array. It can be visualized as an array of single-dimensional arrays. Let’s see how to make it in C++.

Creating a two-dimensional array

A 2-D array can be represented as rows and columns. The following syntax is used to declare a 2-D array.

DataType array_name[row_size][column_size];

For example:

int TwoDArray[4][3]; 

Here, a 2-D array named TwoDArray is being declared with 4 rows and 3 columns. It can be visualized as an array having 4 elements and each element is a single dimension array of size 3; so in total, it contains 12 values.

The TwoDArray array may be viewed logically as the following:

Note: We will shortly discuss how these two-dimensional arrays are stored and how the compiler does the exact mapping of a two-dimensional array to real physical memory.

Initializing a 2-D array

A 2-D array can be initialized in a number of ways.

Using nested braces

Since a 2-D array is an array of 1-D arrays, we can use nested curly braces to group the different levels of elements.

For instance, in the example below, we have a 2-D array of 5 rows and 3 columns. We can see five elements inside the outer curly braces, which can be viewed as rows. Each of these row elements contains three elements that can be viewed as the columns that are being initialized.

int TwoDArray[5][3] = { {0,1,2}, {1,6,4}, {2,9,5}, {3,9,6}, {4,9,4}};

Initializing within a single pair of braces

Can you guess how the arrays will be initialized with the following expression?

int TwoDArray[3][2]={42, 81, 72};

Here, the first three indices of the 2-D array will be initialized with 42, 81, and 72, while the remaining three indices will be initialized with 0. So the first row will be {42, 81}, the second row will be {72, 0}, and the third row will be {0,0}.

Though this expression does not have the nested curly braces for the different dimensions, the initialization using this expression is also valid.

This is possible because the compiler stores a multi-dimensional array in the form of a 1-D array at the backend.

As long as the number of values does not exceed the size of the array, all values are assigned in the increasing subscript of the last dimension by the compiler. We will be able to understand this better when we see how array elements can be accessed in the lesson below.

Initializing with zeros

Like in a one-dimensional array, when we don’t explicitly initialize the array or write anything within the braces, the elements are initialized with 0s. For example:

int TwoDArray[5][3] = { {0,1,2}, {1, 6}, {2}, {3,9,6}, {} };

Here, the second row’s third column will be initialized to 0, and the third row’s second and third columns will also be initialized to 0. In the fifth row, all three columns will be initialized to 0.

In short, the elements not explicitly initialized are initialized to zero.

What about the following expression?

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

Here, the first column of every row will be initialized to 0, 1, 2, 3, and 4, respectively. The rest will all be initialized to 0.

Click “Run” to see the output of this example.

Press + to interact
#include <iostream>
using namespace std;
int main() {
int TwoDArray[5][3] = { {0}, {1}, {2}, {3}, {4} };
// printing the matrix
for(int r=0; r<5; r++) // for each row
{
for(int c=0; c<3; c++) // for each column
{
cout << TwoDArray[r][c] << " "; // printing the values
}
cout << endl;
}
return 0;
}

Explicitly initializing certain elements

Like in a simple array, we can also explicitly initialize certain indices. For example, in the TwoDArray matrix, we can initialize the following indices like this:

int TwoDArray[4][3]={};// this will initialize the entire array with value = 0
// second row, third column
TwoDArray[1][2] = 3;
// first row, first column
TwoDArray[0][0] = 9;

The matrix will be initialized as in the image shown below.

Based on the TwoDArray matrix shown in the image above, solve the quiz below.

1

What will be the result of the expression TwoDArray[0][5] = 6;?

A)

It’s a logical error.

B)

Value 3 will be replaced by 6 in the second row, third column.

Question 1 of 50 attempted

Below are some example programs based on what we learned above. Let’s run all programs one by one to see the outputs.

Initializing a 2-D array with user input

Below is a program that takes numbers as input from the user to initialize two matrices, adds the result inside a new matrix, and prints the result.

#include <iostream>
using namespace std;

int main()
{
    const int rows = 2;  
    const int cols = 2;  

    int matrixA[rows][cols], matrixB[rows][cols], result[rows][cols];

    cout << "Enter 4 integer numbers for a 2x2 matrix A = " << endl;
    for(int r=0; r<rows; r++)
    {
        for(int c=0; c<cols; c++)
        {
            cin >> matrixA[r][c];
        }
    }

    cout << "Enter 4 integer numbers for a 2x2 matrix B = " << endl;
    for(int r=0; r<rows; r++)
    {
        for(int c=0; c<cols; c++)
        {
            cin >> matrixB[r][c];
        }
    }

    cout << endl;
    // storing the sum of the matrices in result[][] matrix
    for(int r=0; r<rows; r++)
    {
        for(int c=0; c < cols; c++)
        {
            result[r][c] = matrixA[r][c] + matrixB[r][c];
        }
    }

    cout << endl;
    // printing the result[][] matrix
    cout << "A+B= " << endl;
    
    for(int r=0; r<rows; r++)
    {
        for(int c=0; c<cols; c++)
        {
           cout << result[r][c] << " ";
        }
        cout << endl;
    }
    return 0;
}




Initializing with 2d array

Accessing and mapping functions

Though we represent a 2-D array in terms of rows and columns, a multidimensional array is practically stored like a 1-D array. So the memory allocation is linear.

Say we have a 2-D array with nn rows and 44 columns; then, at the back end, this array will be stored in the form of a 1-D array with a total of n4n * 4 locations (based on the array data type).

Let’s see how multi-dimensional arrays are mapped to a single-dimensional array. There are two ways to map the elements:

  • Row major mapping
  • Column major mapping

Since C++ follows the row-major layout, let’s look at row-major mapping.

The general formula that the compiler uses is:

baseAddress + rir_i * cols + cic_{i}

Where baseAddress is the address of the array, cols is the number of columns of the array, and cic_i and rir_i are the rows and column indices, respectively, of the element that we want to access or initialize.

Say we wanted to access the element at [1][2], which is 22 in the arr array.

The compiler does this via the following steps:

  1. Take the base address of array arr.

  2. Take the product of the row index ri with the number of columns, i.e. cols which is equal to ri*cols (e.g. for ri=1, it will be 14=41 * 4 = 4). This is like skipping rir_i rows.

  3. Add the outcome to the column index cic_i i.e. (4 + 2 = 6).

  4. Multiply the result by the size of the data type (which is 4 bytes for integers), i.e. 64=246 * 4 = 24, which is equivalent to 0x0018 in hexadecimal.

  5. Finally, add the above result to the array base address (arr + 0x0018)

Since the base address is 0xb0, so 0xb0 + 0x0018 = 0xc8, which is exactly where value 5 is in our arrays.


Passing a multidimensional array to a function

We can pass a multidimensional array to a function like a simple 1D array.

To pass an array to a function, when passing the parameters in the function definitions, we write the name of the array along with two square brackets (e.g. int Array[][maxCols]) to tell the compiler that it's a 2-D array.

For the remainder of this chapter, we have made the following two auxiliary functions for 2-D arrays:

The load2DArrayfunction

This function reads a matrix from the passed ifstream into the passed Array and also sets the rows and columns accordingly.

void load2DArray(ifstream &rdr, int Array[][maxCols], int &rows, int &cols)
{
rdr >> rows >> cols;
for (int r = 0; r < rows; r++)
{
for (int c = 0; c < cols; c++)
{
rdr >> Array[r][c];
}
}
}
load2DArray function

The print2DArrayfunction

This function prints the char array MSG and then prints out the passed Array.

void print2DArray(const char MSG[], int Array[][maxCols], int rows, int cols)
{
cout << endl << MSG << endl;
for (int r = 0; r < rows; r++)
{
cout << "\t\t";
for (int c = 0; c < cols; c++)
{
cout << setw(3) << Array[r][c];
}
cout << endl;
}
}
print2DArray function

When we call the functions, we only write the array's name as the argument to pass to the entire array, as shown in the code below:

Press + to interact
main.cpp
Matrix.txt
#include <iostream>
#include <iomanip>
#include <fstream>
using namespace std;
#include <math.h>
#define maxRows 100 // maxRows is a macro will be replaced by 100
#define maxCols 100 // maxCols is a macro will be replaced by 100
void load2DArray(ifstream &rdr, int Array[][maxCols], int &rows, int &cols);
void print2DArray(const char MSG[], int Array[][maxCols], int rows, int cols);
int main()
{
int Matrix1[maxRows][maxCols], r1, c1;
ifstream rdr("Matrix.txt");
load2DArray(rdr, Matrix1, r1, c1);
print2DArray("Matrix 1:", Matrix1, r1, c1);
return 0;
}

Notice that the parameter int Array[][maxCols] in the function definition has the number of columns, or maxCols, specified but not the number of rows.

It is not necessary (or even recommended) to specify the number of rows. Even if we mention the total rows, the compiler ignores it altogether, just like in the case of a single-dimension array. Still, the number of columns should always be specified. Can you tell why this is so?

In the code above, we have a file called "Matrix.txt" in which we have a 5x6 matrix. We read the matrix values from this file with the help of the fstream header file. The fstream library allows us to handle files in C++. We can create, read, and write inside a file using this header file.

But before we do that, recall that we used to set a maximum capacity for the array, which was more than the array size. We've done the same here, except we've used macros (in lines 6 and 7) to write maxRows and maxCols. Wherever a macro name (maxRows or maxCols) is used within the code, the compiler replaces it with the macro definition (i.e., 100 here). The values of macros cannot be changed during runtime because they are not variables; they are constants. We use the #define directive to define macros.