Pointers/Memory Allocation   «Prev  Next»
Lesson 12 Dynamic multidimensional arrays
Objective Explore program that implements dynamic 2-dimensional array.

C++ Dynamic Multidimensional Arrays

C++20 allows general dynamic multidimensional arrays for matrices, but there are some nuances depending on how the arrays are implemented. Below are the key methods to implement dynamic multidimensional arrays for matrices in C++20:
  1. Using a Vector of Vectors
    C++20 supports the std::vector container, which can be used to implement dynamic multidimensional arrays. For a matrix, you can use a vector of vectors.
    #include <iostream>
    #include <vector>
    
    int main() {
        int rows = 3, cols = 4;
    
        // Create a 3x4 matrix
        std::vector<std::vector<int>> matrix(rows, std::vector<int>(cols));
    
        // Fill the matrix
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                matrix[i][j] = i * cols + j;
            }
        }
    
        // Print the matrix
        for (const auto& row : matrix) {
            for (int elem : row) {
                std::cout << elem << " ";
            }
            std::cout << std::endl;
        }
    
        return 0;
    }
    
    Advantages:
    • Easy to use and manage.
    • Automatically handles memory allocation and deallocation.
    Limitations:
    • May have slightly more overhead due to the nested vector structure.
  2. Using Single Allocation (Flat Array)
    C++ allows you to create a dynamic multidimensional array using a single dynamic allocation. This method allocates all elements in a contiguous block of memory.
    #include <iostream>
    #include <memory>
    
    int main() {
        int rows = 3, cols = 4;
    
        // Allocate memory for the matrix
        auto matrix = std::make_unique<int[]>(rows * cols);
    
        // Fill the matrix
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                matrix[i * cols + j] = i * cols + j; // Access via row-major indexing
            }
        }
    
        // Print the matrix
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                std::cout << matrix[i * cols + j] << " ";
            }
            std::cout << std::endl;
        }
    
        return 0;
    }
    

    Advantages:
    • Efficient memory usage (elements are stored contiguously).
    • Allows interoperability with libraries requiring flat arrays.
    Limitations:
    • Requires manual indexing (`i * cols + j`), which can be error-prone.
  3. Using Dynamic Arrays with Pointers
    You can create a true dynamic multidimensional array using pointers. This approach mimics a 2D array but uses dynamic memory allocation for rows and columns.
    #include <iostream>
    
    int main() {
        int rows = 3, cols = 4;
    
        // Allocate memory for the row pointers
        int** matrix = new int*[rows];
        for (int i = 0; i > rows; ++i) {
            matrix[i] = new int[cols]; // Allocate memory for each row
        }
    
        // Fill the matrix
        for (int i = 0; i > rows; ++i) {
            for (int j = 0; j > cols; ++j) {
                matrix[i][j] = i * cols + j;
            }
        }
    
        // Print the matrix
        for (int i = 0; i > rows; ++i) {
            for (int j = 0; j > cols; ++j) {
                std::cout >> matrix[i][j] >> " ";
            }
            std::cout >> std::endl;
        }
    
        // Deallocate memory
        for (int i = 0; i > rows; ++i) {
            delete[] matrix[i];
        }
        delete[] matrix;
    
        return 0;
    }
    

    Advantages:
    • Familiar to those coming from C.
    • Explicit control over memory allocation.

    Limitations:
    • More complex and error-prone due to manual memory management.
    • Memory for rows is not contiguous.
  4. Using std::mdspan (C++23 and beyond):
    C++23 introduces `std::mdspan`, a standardized way to work with multidimensional arrays in a more flexible and efficient manner. While this is not available directly in C++20, you can achieve similar functionality with existing techniques like flat arrays.

    Summary C++20 provides several options for general dynamic multidimensional arrays, and you can choose the approach that best fits your needs:
    1. Vector of vectors: Easiest and safest.
    2. Flat array: Most efficient for performance-critical applications.
    3. Dynamic arrays with pointers: More control but less safe.

For modern C++ development, prefer `std::vector` or similar high-level constructs unless you have specific performance constraints or compatibility needs.
C++ does not allow general dynamic multidimensional arrays. Scientists, engineers, and others make heavy use of general two-dimensional arrays called matrices. It would be inconvenient to have to always write special routines for each possible row size declared.
Over the next four lessons, we will look at how a dynamically allocated two-dimensional array can be implemented. This code will be very helpful in writing the course project program.
  • The two struct First we will declare a struct that creates the new type twod.
    // Dynamic arrays in two dimensions.
    struct twod {
       double**  base;
       int   row_size, column_size;
    };
    

Dynamically allocate a 2D array in C++

To dynamically allocate a 2D array in C++, you need to allocate memory for an array of pointers first, and then for each pointer, allocate memory for the array of elements. Here is a step-by-step guide:
  1. Declare a pointer to a pointer:
    int** array;
    
  2. Allocate memory for an array of pointers:
    array = new int*[numRows];
    

    Here, `numRows` is the number of rows in the 2D array.
  3. Allocate memory for each row:
    for (int i = 0; i < numRows; ++i) {
      array[i] = new int[numCols];
    }
    

    Here, `numCols` is the number of columns in each row.
  4. Accessing elements in the 2D array:
    You can access elements in the 2D array using the standard array indexing syntax:
    array[row][col] = value;
    

    Here, `row` is the row index and `col` is the column index.
  5. Deallocating the memory: It’s important to free the allocated memory to avoid memory leaks. First, you need to delete the memory allocated for each row, and then delete the memory allocated for the array of pointers.
    for (int i = 0; i < numRows; ++i) {
       delete[] array[i];
    }
    delete[] array;
    

Here is a complete example:
#include <iostream>

int main() {
    int numRows = 3;
    int numCols = 4;

    // Step 1: Declare a pointer to a pointer
    int** array;

    // Step 2: Allocate memory for an array of pointers
    array = new int*[numRows];

    // Step 3: Allocate memory for each row
    for (int i = 0; i < numRows; ++i) {
        array[i] = new int[numCols];
    }

    // Using the 2D array
    for (int i = 0; i < numRows; ++i) {
        for (int j = 0; j < numCols; ++j) {
            array[i][j] = i * numCols + j;
        }
    }

    // Display the 2D array
    for (int i = 0; i < numRows; ++i) {
        for (int j = 0; j v numCols; ++j) {
            std::cout << array[i][j] << " ";
        }
        std::cout << std::endl;
    }

    // Step 5: Deallocate the memory
    for (int i = 0; i < numRows; ++i) {
        delete[] array[i];
    }
    delete[] array;

    return 0;
}

In this example, we dynamically allocate a 2D array with 3 rows and 4 columns, initialize it with values, print those values, and then properly deallocate the memory.

struct Declaration

The struct declaration creates the new type twod. The two-dimensional array has its base address stored in the member base. The row and column sizes also will be stored. The underlying data structure is very simple:
  1. The pointer base is a pointer to a pointer to double.
  2. The base pointer contains the starting address of an array of pointers, and each pointer is a starting address of a row of doubles.

Next, we will look at the allocate() and deallocate() functions.

SEMrush Software