Динамическими в C++ могут быть не только одномерные массивы, но также и многомерные. Из тех, что входят в класс последних, рассмотрим двумерные динамические массивы (динамические матрицы). Ключевым объектом для работы с ними являются указатели.
Использование указателей для работы с динамическими матрицами
При работе с динамическими матрицами в C++ можно использовать обычные указатели. После описания указателя, необходимо будет выделить память для хранения NxM элементов (N — число строк, M — число столбцов). Так выделяется память для хранения целочисленной матрицы размером NxM:
int *A, n, m; A=(int *) calloc (N*M, sizeof(int));
Для выделения памяти можно использовать также и функцию malloc:
A=(int *) malloc (N*M*sizeof(int));
или операцию new:
A=new int (N*M);
После выделения памяти, остается найти способ обратиться к элементу матрицы. Все элементы двумерного массива хранятся в одномерном массиве размером NxM элементов. Сначала в этом массиве расположена 0-я строка матрицы, затем 1-я и т. д. Поэтому для обращения к элементу Ai, j необходимо по номеру строки i и номеру столбца j вычислить номер элемента k в динамическом массиве. Учитывая, что в массиве элементы нумеруются с нуля k = iM + j, обращение к элементу A[i] [j] будет таким *(A + i*m+j).
В качестве примера работы с динамическими матрицами рассмотрим задачу.
Задача
Заданы две матрицы матрицы вещественных чисел A(N, M) и B(N, M). Вычислить матрицу C = A + B.
Решение
Как известно, суммой матриц одинаковой размерности называется матрица, элементы которой получаются сложением соответствующих элементов исходных матриц. Рассмотрим блок-схему алгоритма сложения матриц.
Код программы сложения матриц:
#include "stdafx.h" #include <iostream> using namespace std; int main() { setlocale (LC_ALL, "RUS"); int i, j, N, M; double *a,*b,*c; //ввод размеров матрицы cout<<"N = "; cin>>N; cout<<"M = "; cin>>M; //выделение памяти для матриц a=new double[N*M]; b=new double[N*M]; c=new double[N*M]; //ввод матрицы А cout<<"введите матрицу А"<<endl; for (i=0; i<N; i++) for (j=0; j<M; j++) { cin>>*(a+i*M+j); } //ввод матрицы B cout<<"введите матрицу B"<<endl; for (i=0; i<N; i++) for (j=0; j<M; j++) { cin>>*(b+i*M+j); } //вычисление матрицы C=A+B for (i=0; i<N; i++) for (j=0; j<M; j++) *(c+i*M+j)=*(a+i*M+j)+*(b+i*M+j); //ввод матрицы С cout<<"матрица С:"<<endl; for (i=0; i<N; cout<<endl, i++) for (j=0; j<M; j++) cout<<*(c+i*M+j)<<"\t"; //освобождение памяти delete []a; delete []b; delete []c; system("pause"); return 0; }
Использование двойных указателей для работы с динамическими матрицами
Основной способ работы с динамическими матрицами базируется на использовании двойных указателей. Рассмотрим следующий фрагмент программы.
int main() { int N, M; float **a; a=new float *[N]; }
С помощью оператора new создан массив из N элементов, каждый из которых является адресом, где хранится указатель на float. Осталось определить значение этого указателя. Для этого организуем цикл от 0 до N-1, в котором каждый указатель будет адресовать участок памяти, где хранится M элементов типа float.
for (i=0; i<N; i++) a[i]=new float [M];
После этого определен массив N указателей, каждый из которых адресует массив из M вещественных чисел (типа float). Фактически создана динамическая матрица размера NxM. Обращение к элементу динамической матрицы проходит так же, как и к элементу статической матрицы. Для обращение к элементу ai, j в программе на C++ необходимо указать ее имя, и в квадратных скобках номер строки и столбца (a[i] [j]).
Задача
Написать программу умножения двух матриц вещественных чисел A(N, M) и A(M, L).
Решение
Программа перемножения двух таких матриц, реализованных при помощи двойных указателей, будет выглядеть так:
#include "stdafx.h" #include <iostream> using namespace std; int main() { setlocale (LC_ALL, "RUS"); int i, j, k, N, M, L; //описание двойных указателей для матриц A, B, С double **a,**b,**c; //ввод размерности матриц cout<<"N = "; cin>>N; cout<<"M = "; cin>>M; cout<<"L = "; cin>>L; //выделение памяти для массива а, каждый элемент массива - //указатель на double a=new double *[N]; for (i=0; i<N; i++) //выделение памяти для каждого элемента //a[i], a[i] адресует М элементов типа double a[i]=new double [M]; //выделение памяти для массива b, каждый элемент массива - //указатель на double b=new double *[M]; for (i=0; i<M; i++) //выделение памяти для каждого элемента //b[i], b[i] адресует М элементов типа double b[i]=new double [L]; //выделение памяти для массива c, каждый элемент массива - //указатель на double c=new double *[N]; for (i=0; i<N; i++) //выделение памяти для каждого элемента //c[i], c[i] адресует L элементов типа double c[i]=new double [L]; //ввод матриц А и В cout<<"введите матрицу А"<<endl; for (i=0; i<N; i++) for (j=0; j<M; j++) cin>>a[i][j]; cout<<"введите матрицу B"<<endl; for (i=0; i<M; i++) for (j=0; j<L; j++) cin>>b[i][j]; //умножение матриц A и B for (i=0; i<N; i++) for (j=0; j<L; j++) //цикл для умножения i-й строки матрицы А на j-й столбец матрицы В for (c[i][j]=0, k=0; k<M; k++) c[i][j]+=a[i][k]*b[k][j]; //вывод матрицы С cout<<"Матрица С "<<endl; for (i=0; i<N; cout<<endl, i++) for (j=0; j<L; j++) cout<<c[i][j]<<"\t"; for (i=0; i<N; i++) //освобождение памяти для каждого элемнта a[i] delete [] a[i]; //освобождение памяти для а delete []a; for (i=0; i<M; i++) //освобождение памяти для каждого элемнта b[i] delete [] b[i]; //освобождение памяти для b delete [] b; for (i=0; i<N; i++) //освобождение памяти для каждого элемнта c[i] delete [] c[i]; //освобождение памяти c delete []c; system("pause"); return 0; }