Шаг 41.
Массивы динамической памяти

    На этом шаге мы рассмотрим динамические массивы.

    Операция new при использовании с массивами имеет следующий формат:

     new <тип массива>.  

    Такая операция позволяет выделить в динамической памяти участок для размещения массива соответствующего типа, но не позволяет его инициализировать. В результате выполнения операция new возвратит указатель, значением которого служит адрес первого элемента массива. При выделении динамической памяти для массива его размеры должны быть полностью определены, например:

     long (*lp)[2] [4] ; // Определили указатель.
     lp = new long[3][2][4] ; // Выделили память для массива.

    В данном примере использован указатель на объекты в виде двумерных массивов, каждый из которых имеет фиксированные размеры 2 на 4 и содержит элементы типа long. В определении указателя следует обратить внимание на круглые скобки, без которых обойтись нельзя. После выполнения приведенных операторов указатель lp становится средством доступа к участку динамической памяти с размерами 3 * 2 * 4 * sizeof(long) байтов. В отличие от имени массива (имени у этого массива из примера нет) указатель lp есть переменная, что позволяет изменять его значение и тем самым, например, перемещаться по элементам массива.

    Изменять значение указателя на динамический массив нужно осторожно, чтобы не "забыть", где находится начало массива, так как указатель, значение которого определяется при выделении памяти для динамического массива, используется затем для освобождения памяти с помощью операции delete. Например, оператор:

     delete [] lp; 

освободит целиком всю память, выделенную для определенного выше трехмерного массива, если lp адресует его начало.

    B отличие от определения массивов, не размещающихся в динамической памяти, инициализация динамических массивов не выполняется. Поэтому при выделении памяти для динамических массивов их размеры должны быть полностью определены явно. Кроме того, только первый (самый левый) размер массива может быть задан с помощью переменной. Остальные размеры многомерного массива могут быть определены только с помощью констант. Это затрудняет работу с многомерными динамическими массивами. Обойти это ограничение позволяет применение массивов указателей.

    Проиллюстрируем приведенные здесь теоретические положения конкретными примерами.


    Пример 1. Создание динамического массива с использованием констант, задающих размерность массива.
#include<iostream.h>
void main ()
{
  int const N=2; //Константы,
  int const M=4; //задающие размерность
  int const L=3; //массива.
  /*-------------------*/
  int (*ip)[M][L], //Описание указателя.
		 x=0;  //Переменная для заполнения массива.
  ip= new int [N][M][L]; //Выделение памяти из кучи.
  //Заполнение массива.
  for (int i=0;i<N;i++)
	 for (int j=0;j<M;j++)
		for (int k=0;k<L;k++)
			ip[i][j][k]=++x;
  //Вывод массива.
  cout << "Полученный массив:" << endl;
  for (i=0;i<N;i++)
  {
	 cout << i << " строка: " << endl;
	 for (int j=0;j<M;j++)
	 {
		 cout << '\t' << j << " столбец: " << endl;
		 cout << "\t\tНаходящиеся там значения: " ;
		 for (int k=0;k<L;k++)
			cout << ip[i][j][k] << "  ";
		 cout << endl;
	 }
  }
  delete [] ip;//Возврат памяти в кучу.
}
Текст этой программы можно взять здесь.

    В этом примере при описании указателя на массив используются константы, задающие размерность создаваемого массива. Однако так бывает далеко не всегда. Как правило, размерность создаваемого массива становится известной только в момент выполнения программы. Следующий пример иллюстрирует возможность определения одной размерности массива в момент выполнения программы.


    Пример 2. Создание динамического массива с использованием ввода одной размерности.
#include<iostream.h>
void main ()
{
//  int const N=2;
  int const M=4;
  int const L=3;
  /*-------------------*/
  int (*ip)[M][L],x=0,N;
  cout << "Количество строк массива: ";
  cin >> N;
  ip= new int [N][M][L];

  for (int i=0;i<N;i++)
	 for (int j=0;j<M;j++)
		for (int k=0;k<L;k++)
			ip[i][j][k]=++x;
  cout << "Полученный массив:" << endl;
  for (i=0;i<N;i++)
  {
	 cout << i << " строка: " << endl;
	 for (int j=0;j<M;j++)
	 {
		 cout << '\t' << j << " столбец: " << endl;
		 cout << "\t\tНаходящиеся там значения: " ;
		 for (int k=0;k<L;k++)
			cout << ip[i][j][k] << "  ";
		 cout << endl;
	 }
  }
  delete [] ip;
}
Текст этой программы можно взять здесь.

    В этом примере количество строк вводится с клавиатуры при выполнении программы. А если нужно задать не одну, а две, три размерности? В этом случае такой вариант решения не проходит:

.   .   .   .   .
//  int const N=2;
//  int const M=4;
  int const L=3;
  /*-------------------*/
  int x=0,N,M;
  cout << "Количество строк массива: ";
  cin >> N;
  cout << "Количество столбцов массива: ";
  cin >> M;
  int (*ip)[M][L];
  ip= new int [N][M][L];
.   .   .   .   .

в связи с тем, что конструкции, используемые при описании указателя, должны быть константами! В этом случае можно поступить следующим образом:

  1. выделить память для одномерного массива для размещения всех элементов многомерного массива;
  2. в тексте программы осуществлять доступ к этому массиву как к многомерному. Например, если ip - указатель на такой одномерный массив, а доступ к нему нужно осуществлять как к трехмерному ip[N][M][L], то доступ к элементу ip[i][j][k] можно записать следующим образом: *(ip+i*(M*L)+j*L+k). Если ip - указатель на одномерный массив, а доступ к нему нужно осуществлять как к двумерному ip[N][M], то доступ к элементу ip[i][j] можно записать следующим образом: *(ip+i*M+j).


    Пример 3. Создание динамического массива с использованием ввода всех размерностей.
#include<iostream.h>
void main ()
{
  int *ip,x=0,N,M,L;
  cout << "Количество строк массива: ";
  cin >> N;
  cout << "Количество столбцов массива: ";
  cin >> M;
  cout << "Количество значений в столбце: ";
  cin >> L;
  //Резервируем место в куче для одномерного массива.
  ip= new int [N*M*L];
  //Заполняем массив значениями.
  for (int i=0;i<N;i++)
    for (int j=0;j<M;j++)
	for (int k=0;k<L;k++)
		 //Моделируем доступ к многомерному массиву.
		*(ip+i*(M*L)+j*L+k)=++x;
  cout << "Полученный массив:" << endl;
  for (i=0;i<N;i++)
  {
	 cout << i << " строка: " << endl;
	 for (int j=0;j<M;j++)
	 {
		 cout << '\t' << j << " столбец: " << endl;
		 cout << "\t\tНаходящиеся там значения: " ;
		 for (int k=0;k<L;k++)
			cout << *(ip+i*(M*L)+j*L+k) << "  ";
		 cout << endl;
	 }
  }
  delete [] ip;
}
Текст этой программы можно взять здесь.

    Со следующего шага мы начнем знакомиться с функциями.


Предыдущий шаг Содержание Следующий шаг