Static Two-Dimensional Array
Learn how to use the static two-dimensional array in C++.
Multidimensional arrays
A multidimensional array is an array that contains one or more arrays, where each array within the multidimensional array can be seen as an element of the outer array. In other words, a
For instance, a 2D (two-dimensional) array can be visualized as an array of single-dimensional arrays.
Let’s learn how to make these multidimensional arrays in C++. For now, we’ll only focus on 2D arrays. We can easily generalize and extend the same syntax by adding further dimensions.
Creating a 2D array
A 2D array is often represented as a matrix with rows and columns. To declare a 2D array in C++, the following syntax is typically used:
DataType array_name[row_size][column_size];
For instance, consider the following example:
int TwoDArray[4][3];
This declares a 2D array named TwoDArray
with 4
rows and 3
columns. We can visualize it as an array that has four elements, where each element is a 1D array of size three. In total, the array contains 12 values.
The TwoDArray
array may be viewed logically as follows:
Initializing a 2D array
Since a 2D array is an array of 1D arrays, we can use nested curly braces ({{...}}
) to group the different levels of elements.
For instance, in the example below, we have a 2D array of 4
rows and 3
columns. We can see four elements inside the outer curly braces, which can be viewed as rows. Each row element contains three elements that can be viewed as the columns being initialized.
int TwoDArray[4][3] = { {0,1,2}, {1,6,4}, {2,9,5}, {3,9,6} };
To effectively utilize a 2D array, it is crucial to understand how to access its elements. This can be achieved by utilizing two subscript ([]
) operators, where the first subscript represents the row number and the second represents the column number.
TwoDArray[r][c] ; // This will access the rth array and take the cth element within that array.
Let’s look at an example.
#include <iostream>using namespace std;int main(){int TwoDArray[4][3] = { {0,1,2}, {1,6,4}, {2,9,5}, {3,9,6} };for(int r = 0; r < 4; r++) // for each row{for(int c = 0; c < 3; c++) // for each columncout << TwoDArray[r][c] << " "; // printing the valuescout << endl;}return 0;}
In the Example2D.cpp
file:
The given code initializes and prints the values of a 2D array
TwoDArray
with dimensions4
(rows) and3
(columns). It uses nestedfor
loops to iterate through each element of the array. The outer loop represents the rows, while the inner loop represents the columns. Thecout
statement is used to print each element, followed by a space. After printing all the elements in a row, thecout << endl;
statement is used to move to the next line, creating a row-wise display of the array.
In the Example2DGoodPractice.cpp
file:
The second code is an improved version of the first implementation because it uses constants (
ROWS
andCOLS
) to define the number of rows and columns in the 2D array. This approach enhances code flexibility and maintainability. By using constants, it becomes easier to modify the array size by simply changing the values of the constants without the need to modify the loop conditions or array declaration directly. This enables more efficient code modification and avoids potential errors when adjusting the array dimensions. Overall, the second code provides a more adaptable and scalable solution compared to the first implementation.
Accessing and mapping functions
Though we logically represent a 2D array in terms of rows and columns, a multidimensional array is physically stored (in RAM) like a 1D array.
For instance, say we have a 2D array with
Let’s see how multidimensional arrays are mapped to a single-dimensional array. There are two ways to map the elements:
Row major mapping (the first row, followed by the second row, the third row, and so on)
Column major mapping (the first column, the second column, the third column, and so on)
In C++, arrays are laid out in memory using a row-major layout. This means that the elements of a row are stored next to each other in memory, and accessing elements in row-major order can improve the cache performance.
To determine the address of a particular element in a 2D array, the following formula translation happens automatically:
Here,
For example, consider the following 2D array:
int TwoDArray[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};
To access the element at row ri
, column ci
in TwoDArray[ri][ci]
, we'll use the following formula:
accessed_address = &TwoDArray[0][0] + (ri * 4 + ci) * sizeof(int)
This formula calculates the address of the element using the base address of the array (&TwoDArray[0][0]
). For example, if we access the element at TwoDArray[2][3]
, the formula will drive:
// singleDArray[] = {1,2,3,4,5,6,7,8,9,10,11,12};accessed_address = &TwoDArray[0][0] + (2 * 4 + 3) * sizeof(int)// Will access the 11th element of the singleDArray i.e. the last element.
Passing 2D arrays to functions
When we pass a 2D array to a function in C++, we need to specify the number of columns in the array in the function header because the function needs to know the size of each row to correctly calculate the address of each element.
We’ll write a print2DArray()
function with these essential parameters:
The first parameter is a
const char msg[]
(anull
terminating string, which will be a message, before printing the matrix).M[][MAXCOLS]
(the matrix) holds all the values that we need to process. Notice that we have writtenMAXCOLS
in columns dimension (that is because the compiler needs to know which conversion formula it should be using to map it to the 1D map).int rows
andint cols
represents the total number of rows and columns that we want to process.
Let’s write the function.
#include <iostream>#include <iomanip>using namespace std;void print2DArray(const char msg[], int M[][4], int rows){cout << msg << '\n';for (int r = 0; r < rows; r++){for (int c = 0; c < 4; c++)cout << M[r][c] << " ";cout << '\n';}}void print2DArray(const char msg[], int M[][2], int rows){cout << msg << '\n';for (int r = 0; r < rows; r++){for (int c = 0; c < 2; c++)cout << M[r][c] << " ";cout << '\n';}}void print2DArray(const char msg[], int M[][3], int rows){cout << msg << '\n';for (int r = 0; r < rows; r++){for (int c = 0; c < 3; c++)cout << M[r][c] << " ";cout << '\n';}}int main(){int arr[4][4] = {{1, 2, 3, 4}, {1, 2, 3, 4}, {3, 3, 3, 3}, {4, 4, 4, 4}};// Only 2x2 values are initialized the rest will automatically assigned to 0print2DArray("4x4 Matrix:", arr, 4);int arr2[2][2] = {{1, 2}, {3, 4}};// Only 2x2 values are initialized the rest will automatically assigned to 0. Along with// that we have taken two variables rows, cols holding how much of the sub matrix must be usedprint2DArray("2x2 Matrix:", arr, 2);int arr3[3][3] = {{1, 2, 3}, {3, 4, 5}, {5, 6, 7}};// Only 2x2 values are initialized the rest will automatically assigned to 0print2DArray("3x3 Matrix:", arr3, 3);return 0;}
In the multipleFunctionsDifferentCols.cpp
file:
- Multiple
print2DArray()
functions are defined, each accepting a different column dimension of a 2D array. This is necessary because the compiler needs to determine the appropriate formula to map the 2D array onto a single dimension at compile-time. Since the column dimension affects the calculation of memory addresses, distinct functions are required to handle each specific column dimension. By providing separate functions, the program ensures that the correct mapping and printing of the 2D array elements are performed accurately for different column dimensions.
In the oneFunctionsDifferentDimensions.cpp
file:
- With the provided implementation of
print2DArray()
, theMAXCOLS
parameter represents the fixed column dimension of the array, while therows
andcols
parameters specify the subwindow or subarray of the larger 2D array that the function will manipulate. By using these variables, the function can dynamically handle different subwindow sizes without the need for separate functions. This approach provides flexibility by allowing the function to work with various column dimensions while maintaining a single function definition.
Let’s practice with more problem-solving exercises using 2D arrays to sharpen our skills and gain proficiency in handling them.
Example 1: Column-wise sum
In this example, we’ll write a sumOfAllColumns()
function that takes in a 2D array M
, the number of rows and columns to process, and an array colsSum
to store the sum of each column.
void sumOfAllColumns(int M[][MAXCOLS], int rows, int cols, int colsSum[]);// M is the 2D-matrix, rows and cols are the subwindow where we want to work and// colsSum[] is passed as an array where the answers should be stored.
The function will compute the sum of each column in the 2D array M
and store the results in the colsSum[]
array. Let’s proceed with implementing and testing the function.
#include <iostream>#include <iomanip>using namespace std;const int MAXROWS=2, MAXCOLS = 2; // Global Variable// Assume the following implementations are availablevoid print(const char Msg[], int A[], int size); // for printing a 1D-arrayvoid print2DArray(const char msg[], int M[][MAXROWS], int rows, int cols); // for printing a 2D-arrayvoid sumOfAllColumns(int M[][MAXCOLS], int rows, int cols, int colsSum[]){for (int c = 0; c < cols; c++){colsSum[c] = 0; // Initialize the summation at colsSum[c] to 0.for (int r = 0 ; r < rows; r++)colsSum[c] = colsSum[c]+ M[r][c];}}int main(){int arr[MAXROWS][MAXCOLS] = {{1, 2}, {3, 4}};int rows = 2, cols = 2;int arr1[MAXCOLS]={0}, size = cols;print2DArray("2x2 Matrix:", arr, rows, cols);sumOfAllColumns(arr, rows, cols, arr1);print("Sum of the columns: ", arr1, size);return 0;}
In the
sumOfAllColumns()
function, the loop iterates through each column, and then for each column, it iterates through each row to calculate the sum of elements in that column. This approach is chosen because it allows us to calculate the sum of each column separately.By iterating through each column first, we can focus on a single column at a time and calculate its sum by iterating through the corresponding elements in each row. This ensures that we calculate the sum of each column accurately without mixing the elements from different columns. This approach is necessary because calculating column sums requires accessing elements column-wise rather than row-wise. Therefore, it is more logical and efficient to iterate through each column and then iterate through each row within that column to perform the summation.
Tip: Change the values of the 2D array
M
and test it on several inputs to better understand how the program is executed.
Practice exercise: Row-wise summation
Change the above code so that it now calculates row-wise summation. What changes do we need to make? Think and update the code and test it.
Let’s practice with another example of sorting matrix rows and columns.
Example 2: Row-wise sorting
Let’s implement the rowWiseSorting()
function, which sorts a 2D array twoDarr[]
with a specific number of rows and columns. The function sorts odd-numbered rows in ascending order and even-numbered rows in descending order. To enhance reusability, we’ll also include asc()
and dsc()
functions for sorting a single dimension in ascending and descending order, respectively. Here’s the implementation:
#include <iostream>using namespace std;const int MAXROWS=5, MAXCOLS = 5;void print2DArray(const char msg[], int M[][MAXCOLS], int rows, int cols);void asc(int row[], int size){for(int i = 0; i<size - 1; i++)for(int j = 0; j < size - i - 1; j++)if(row[j]>row[j+1])swap(row[j], row[j+1]);}void dsc(int row[], int size){for(int i = 0; i<size-1; i++)for(int j = 0; j < size - i - 1; j++)if(row[j]<row[j+1])swap(row[j], row[j+1]);}void rowWiseSort(int twoDarr[][MAXCOLS], int rows, int cols){for(int i = 0; i < rows ;i++){if(i % 2)asc(twoDarr[i], cols); // passing twoDarr[i] is sending a row to asc functionelsedsc(twoDarr[i], cols); // passing twoDarr[i] is sending a row to dsc function}}int main() {int twoDarr[MAXROWS][MAXCOLS]= {{13, 16, 18 , 15, 12 },{24, 29, 19, 12, 10},{10, 34, 22 , 18 , 11}, {11, 51, 17, 41, 32},{22, 61, 43, 11, 91}};int row = 5;int col = 5;print2DArray("Before sorting", twoDarr, row, col);rowWiseSort(twoDarr, row, col);print2DArray("After sorting", twoDarr, row, col);return 0;}
The provided code contains the
rowWiseSort()
function, which sorts a 2D arraytwoDarr
row-wise. The function iterates through each row of the array using afor
loop. For odd-numbered rows, it calls theasc()
function and passes the corresponding row (twoDarr[i]
) as a single dimension array, along with the number of columns (cols
), to sort it in ascending order. Similarly, for even-numbered rows, it calls thedsc()
function to sort the row in descending order.By passing a single dimension array representing a row to the sorting functions, the code achieves reusability because the same functions (
asc()
anddsc()
) can be used to sort any single-dimension array. This approach avoids the need for separate sorting logic for odd and even rows within therowWiseSort()
function, making the code more concise and modular.
Have we achieved the best 2D array?
What is the problem with using MAXCOLS
as a global variable in the above implementation? Take a moment to consider the issue.
The subsequent lessons will discuss dynamic memory allocation, offering insights into the aforementioned question.