Создание нового проекта консольного типа, страница 7

Адрес

Адреса

Серия отдельных одномерных массивов

массива адресов

массивов

Ї

Ї

Ї

a

®

a[0]

®

a[0][0]

a[0][1]

a[0][n-1]

a[1]

®

a[1][0]

a[1][1]

a[1][n-1]

®

a[n-1]

®

a[n-1][0]

a[n-1][1]

a[n-1][n-1]

Эта схема очень похожа на ту, которая была описана словесно для статического двухмерного массива. Отличие состоит в том, что теперь для адресов a, a[0], a[1],..., a[n-1] должно быть отведено реальное физическое пространство памяти, в то время как для статического двухмерного массива выражения вида a, a[0], a[1],..., a[n-1] были лишь возможными конструкциями для ссылок на реально существующие элементы массива, но сами эти указатели не существовали как объекты в памяти компьютера. Алгоритм выделения памяти таков:

¨  Определяем в стеке переменную a как адрес массива адресов: float **a;.

¨  Захватываем память из области heap для массива из n указателей на тип float и присваиваем адрес начала этой памяти указателю a. Оператор, выполняющий это действие, выглядит так:

 a = new float* [n];

¨  В цикле пробегаем по массиву адресов a[], присваивая каждому указателю a[i] адрес вновь захватываемой памяти под массив из n чисел типа float.

При работе с динамически задаваемыми массивами начинающие часто забывают освобождать память, захваченную для массива. Память следует вновь возвращать в распоряжение операционной системы, то есть освобождать операцией delete. Правда при завершении работы функции main автоматически уничтожаются все переменные, созданные в программе, и указатели сегментов памяти получают свои исходные значения. Однако, при разработке сложных многомодульных комплексов программ следует помнить о том, что захваченная память повисает, становится недоступной операционной системе при выходе из области действия указателя, который ссылается на ее начало. Это может вызвать отказ в выделении новой памяти в каком-то другом программном модуле, если весь объем области heap будет исчерпан. Операция delete, совместно с операцией new, позволяет контролировать процесс последовательного захвата и высвобождения динамической памяти. Чтобы освободить память, захваченную для одной переменной d, например, с помощью оператора: double *d=new double; достаточно в конце функции или блока, где использовалась переменная d, записать: delete d;. Если был размещен массив переменных, например float *p = new float[200], то в современных версиях компиляторов следует освобождать память оператором  delete [] p;. Здесь квадратные скобки указывают компилятору на то, что освобождать следует то количество ячеек, которое было захвачено последней операцией new в применении к указателю p. Явно указывать это число не нужно. Более ранние версии компиляторов C++ использовали только одну разновидность операции delete (без квадратных скобок) для освобождения памяти, занимаемой как массивом, так и простой переменной. Компилятор Visual C++ 6.0 при попытке освободить память, занятую массивом, операцией delete без скобок не выдает сообщений об ошибке и, по видимости, функционирует верно, однако для обеспечения надежности следует соблюдать условия стандарта. Заметьте, что операция delete игнорирует нулевые указатели, поэтому проверка на неравенство нулю указателя перед тем, как освободить память, на которую он ссылается, является излишней. Полезно поэкспериментировать с такими модулями:

        // Динамический захват и освобождение памяти

double *a;   // Одна переменная

double *d;   // Массив переменных

double **dd; // Двухмерный массив

void GetMem()

{                        // Захват памяти

a = new double;         // Одна переменная

d = new double[4];      // Массив переменных

dd = new double*[3];    // Двухмерный массив

for (int i=0; i<3; i++)

  dd[i] = new double[2];

                         // Присвоение

*a=1.;                  // Одна переменная

printf("\n *a=%2.0f\n",*a);

                         // Массив переменных

printf("\n Array starts from %p  and has\n",d);

for (i=0; i<4; i++)

{

  d[i] = double (i);

  printf("\n d[%d]=%2.0f",i,d[i]);

}

printf("\n\n 2D array starts from %p  and has\n",dd);

for (i=0; i<3; i++, putch('\n')) // Двухмерный массив

  for (int j=0; j<2; j++)

  {

   dd[i][j]=(double)(i+j);

   printf("\n dd[%d][%d]=%2.0f",i,j,dd[i][j]);

  }

}

void FreeMem()

{                        // Освобождение памяти

delete a;               // Одна переменная

delete [] d;            // Массив переменных

for (int i=0; i<3; i++) // Двухмерный массив

  delete [] dd[i];

delete [] dd;

}