Next: Matrices with the Standard Up: Plain matrices in C++ Previous: Linking with Fortran encoded

Dynamically allocated matrices

In the previous sections we have seen how to reserve a fixed amount of storage for a matrix at the time the code is compiled. Suppose later on we find we want space for a larger matrix. We would have to modify the code, increasing the matrix dimensions, and then recompile it. This extra step is avoided if we allocate space for the matrix dynamically. With dynamical allocation we don't have to decide on any limits to the size when we compile the code. Instead, at run time we ask the operating system to give us the space we need, provided there is enough memory available. The program size in computer memory then grows. Dynamical allocation can use memory more efficiently because one can ask just for the space required, and the space can be released when it is no longer needed.

The simplest method for allocating space dynamically is to wait until after we know the actual size and then declare the matrix using variables as dimensions. Here is a short code excerpt showing how this is done:

 ``` cout << "Enter the number of rows in the matrix \n"; cin >> m; cout << "Enter the number of columns in the matrix \n"; cin >> n; double a[m][n], x[n], b[m]; ```

Notice that the declarations for `a`, `x`, and `b`, must come after the values `m` and `n` are known. (And we have been careful to get the correct number of elements for the matrix times vector operation.)

Space for the matrix `a` and vectors `x` and `b` is automatically allocated and it is automatically released when the those variables go out of scope (i.e. execution passes back to a calling program or the program terminates.) The pointer values for `a` and `a[i]` are exactly the same as they would be if we had used constants for the dimensions.

You may use this method for arrays that you are passing to a Fortran coded subprogram, provided the function prototype declaration is written in a compatible way. Here is a way to do it following the above example. The prototype declaration should read

 ``` extern "C"{ void mulmatvec_(int *m, int *n, int *max, double *a, double *x, double *b); } ```

and the subprogram is then called like this:

 ``` mulmatvec_(&m,&n,&n,&a[0][0],x,b); ```

That is, we pass a pointer to the first element of the matrix. Since we allocated the matrix with a second dimension of `n`, we have `max = n`.

If we are not trying to interface with Fortran, we can use a more elegant way to allocate matrices dynamically in C++ that makes it easy to pass arrays to C++ subprograms. Here is a simple example:

 ``` #include using namespace std; void mulmatvec(int m, int n, double **a, double *x, double *b){ int i,j; for(i = 0; i < m; i++){ b[i] = 0.; for(j = 0; j < n; j++) b[i] += a[i][j]*x[j]; } return; } int main(){ int i,j,m,n; cout << "Enter the number of rows in the matrix \n"; cin >> m; cout << "Enter the number of columns in the matrix \n"; cin >> n; // Allocate space for the vectors double *x = new double[n]; double *b = new double[m]; // Allocate space for the matrix double **a = new double* [m]; for(i = 0; i < m; i++) a[i] = new double[n]; if(!x || !b || !a){ cerr << "Can't allocate space\n"; return 1; } cout << "Enter the matrix\n"; for(i = 0; i < m; i++) for(j = 0; j < n; j++) cin >> a[i][j]; cout << "Enter the vector\n"; for(j = 0; j < n; j++) cin >> x[j]; mulmatvec(m,n,a,x,b); cout << "\nA*x = \n"; for(i = 0; i < m; i++) cout << b[i] << "\n"; delete [] a; delete [] b; delete [] x; return 0; } ```

Let us examine the new features of this code.

 ``` double *x = new double[n]; ```

This statement requests space for `n` contiguous double precision values. It declares that `x` is a pointer and initializes it to be the address of the first one. It does essentially the same thing as the declaration

 ``` double x[n]; ```

Next we create the matrix by treating it as `m` separate row vectors of length `n` each. To make the syntax work for us, we also create a list of `m` pointers that point to the first value in each row.
 ``` double **a = new double* [m]; for(i = 0; i < m; i++) a[i] = new double[n]; ```

The first statement requests space for `m` pointers to doubles. And it declares that `a` is a pointer to the first of them, which makes its type a pointer to a pointer to a double - hence the two asterisks. Notice that `a[i]` is then a pointer to a double. For each `i` we then request space for `n` doubles and initialize `a[i]` to be the address of the first one. Altogether we have allocated space for `m*n` doubles and `m` pointers to doubles. Notice that `a[i][j]` is a double. The valid range for `i` is from 0 to `m-1` and for `j` from 0 to `n-1`, which is exactly what we want for the matrix.

Notice that although we refer to the matrix element as `a[i][j]` the meaning of this expression is a bit different from what we get with the static declaration double a[MAX][MAX]. With the dynamic allocation we actually have space allocated for the pointers `a[i]`. With the static declaration, the compiler understands that `a[i]` also points to the first element in a row, but we have not allocated storage for it.

 ``` delete [] a; ```

This statement frees space for both the row vectors as well as the vector of pointers. Without the `[]`, we would be deleting space only for the pointers.

There are variations on this theme. Another method allocates the space in a single block as follows:
 ``` double **a = new double* [m]; a[0] = new double [m*n]; for(i = 1; i < m; i++) a[i] = a[i-1] + n; ```

The for loop calculates the addresses so the `n` matrix elements in row `a[i]` come immediately after the `n` matrix elements in row `a[i-1]`. With this organization we have in effect declared a to be

```   double a[m][n];
```
and with the rows arranged in the correct sequence, it is permissible to use this matrix with Fortran subprograms in the same way as before. So if your application uses the same array in calls to both C++ and Fortran subprograms, this method is the most elegant one.

Next: Matrices with the Standard Up: Plain matrices in C++ Previous: Linking with Fortran encoded
Carleton DeTar 2012-10-22