Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
49
Добавлен:
16.02.2016
Размер:
910.85 Кб
Скачать

Динамическое выделение массивов

В программе каждая переменная может размещаться в одном из трех мест: в области данных программы, в стеке или в свободной памяти (так называемая куча).

Каждой переменной в программе память может отводиться либо статически, то есть в момент загрузки программы, либо динамически - в процессе выполнения программы. До сих пор все определяемые массивы объявлялись статически, и, следовательно, хранили значения всех своих элементов в стековой памяти или области данных программы. Если количество элементов массива невелико, такое размещение оправдано. Однако довольно часто возникают случаи, когда в стековой памяти, содержащей локальные переменные в вспомогательную информацию (например, точки возврата из вложенных функций), недостаточно места для размещения всех элементов большого массива. Ситуация еще более усугубляется, если массивов большого размера должно быть много. Здесь на помощь приходит возможность использования для хранения данных динамической памяти.

Функции malloc, calloc, free и операторы new и delete

Чтобы в дальнейшем можно было разместить в памяти некоторый динамический объект, для него необходимо предварительно выделить в памяти соответствующее место. По окончании работы с объектом выделенную для его хранения память требуется освободить.

Выделение динамической памяти под объект осуществляется при помощи следующих выражений:

• malloc;

• calloc;

• new;

Освобождение выделенных ресурсов памяти производится выражениями:

• free;

• delete;

Функция malloc подключается в одном из заголовочных файлов stdlib.h или alloc.h и имеет синтаксис:

void *malloc(size_t size);

Данная функция призвана выделить в памяти блок размером size байт из кучи. В больших моделях памяти (Compact, Large, Huge) для кучи доступно все пространство памяти от вершины программного стека, В моделях Tiny, Small и Medium это пространство сокращено из-за наличия небольшой системной области, позволяющей стеку расти.

В случае успешного резервирования блока памяти функция malice возвращает указатель на только что выделенный блок.

При неудачном результате операции с памятью функция возвращает NULL - зарезервированный идентификатор со значением ' \0' (абсолютное значение 0x00). При этом содержимое блока остается неизменным. Если в качестве аргумента функции указано значение 0, функция возвращает NULL.

Как видно из синтаксиса, данная функция возвращает указатель типа void, однако, поскольку на практике чаще всего приходится выделять память для объектов конкретного типа, приходится приводить тип полученного пустого указателя к требуемому типу. Например, выделение памяти под три объекта типа int и определение указателя на начало выделенного блока можно произвести следующим образом:

int pint = (int*)malloc(3*sizeof(int));

В отличие от malloc, функция calloc кроме выделения области памяти под массив объектов еще производит инициализацию элементов массива нулевыми значениями. Функция имеет следующий синтаксис:

void *calloc( size_t num, size_t size );

Аргумент num указывает, сколько элементов будет храниться в массиве, а параметр size сообщает размер каждого элемента в байтах. Приведенный здесь тип size_t (объявлен в заголовочном файле stddef .h) является синонимом типа unsigned char, возвращаемого оператором sizeof.

Функция освобождения памяти free в качестве единственного аргумента принимает указатель на удаляемый блок объектов (*block) в имеет синтаксис:

void free(void *block);

Данная функция подключается в заголовочном файле s tdlib. h или alloc. h. Тип объектов удаляемого блока может быть произвольным, на что указывает ключевое слово void.

Рассмотрим пример выделения динамической памяти для десяти объектов типа int, заполнение выделенной области числами от нуля до девяти и освобождение ресурса памяти.

#incl\ide <iostream.h>

tinclude <alloc.h>

int main(void)

{

int* pRegion;

// Выявление области памяти if ((pRegion =

(int*)malloc(10*sizeof (int)))==NULL)

{

cout « "He достаточно памяти\n";

// Завершить программу если не

// хватает памяти для выделения

return 1;

}

// Заполнение выделенного блока памяти for(int i=0; i<10; i++) *(pRegion+i) = i;

// Вывод значений заполненного блока

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

{

cout « (*pRegion) « *\n';

pRegion++; }

// Освобождение блока памяти free(pRegion);

return 0 ;

}

Пример динамического выделения памяти под двумерный массив Array [10] [20] переменных типа int выглядит следующим образом:

Array = (int*)malloc(10*20*sizeof(int));

В отличие от функций работы с динамической памятью malloc, calloc и free, заимствованных в C++ из стандарта ANSI С для совместимости, новые операторы гибкого распределения памяти new и delete обладают дополнительными возможностями, перечисленными ниже.

• Как отмечалось выше, функции malice и calloc возвращают пустой указатель, который в дальнейшем требуется приводить к заданному типу. Оператор new возвращает указатель на тип, для которого выделялась память, и дополнительных преобразований уже не требуется.

• Операция пен предполагает возможность использования совместно с библиотечной функцией set_new_handler, позволяющей пользователю определить свою собственную процедуру обработки ошибки при выделении памяти.

• Операторы new и delete могут быть перегружены с тем, чтобы они, например, могли принимать дополнительные параметры или выполняли специфические действия с учетом конкретной ситуации работы с памятью. Подробно перегрузка операторов приводится в главе 15.

Операторы new и delete имеют две формы:

• управление динамическим размещением в памяти единичного объекта:

• динамическое размещение массива объектов. Синтаксис при работе с единичными объектами следующий:

тит_объекта *имя = new тип__объекта; delete имя;

При управлении жизненным циклом массива объектов синтаксис обоих операторов имеет вид:

тип объекта *имя = new тип_объекта[число]; delete[] имя;

Здесь число в операторе new[] характеризует количество объектов типа тип_объекга, для которых производится выделение области памяти. В случае успешного резервирования памяти переменная-указатель имя ссылается на начало выделенной области. При удалении массива его размер указывать не нужно.

Форма оператора delete должна обязательно соответствовать форме оператора new для данного объекта: если выделение памяти проводилось для единичного объекта (new), освобождение памяти также должно осуществляться для единичного объекта (delete). В случае применения оператора new [ ] (для массива объектов), в конечном итоге высвобождение памяти должно быть произведено с использованием оператора delete [ ].

Поясним все вышесказанное на примере.

#include <iostream.h> #include <new.h>

void newHandler()

{

cout « "\nHeдостаточно памяти!\n";

}

int main()

{

int* ptr;

set_new_handler(newHandler);

ptr = new int[100];

for(int i=0; i<100; i++)*(ptr+i) = 100-i;

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

cout « *(ptr+i) « " ";

delete[] ptr;

long double* 1ptr;

if(Iptr = new long double[999999])

{

cout « "\nГотово!";

delete[] 1ptr; } else

cout « "\nНеудача...";

return 0;

}

В начале представленной программы подключается уже знакомый заголовочный файл iostream.h для использования операций ввода/вывода, а также файл new. h для определения обработчика ошибок set_new_handler. Функция newHandler (). реализованная в начале программы, является собственно обработчиком ошибок выделения памяти. Она подключается в строкеset_new_handler(newHandler);

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

"Недостаточно памяти!".

В главной функции программы следует объявление целочисленного указателя ptr, которому тут же оператором new присваивается адрес выделенного блока динамической памяти для 100 значений типа int. Далее область памяти заполняется в цикле числами от 100 до 1, а в следующем цикле осуществляется вывод ее содержимого, после чего выделенный блок динамической памяти освобождается (оператор delete []). Следующий шаг - попытка выделения памяти для большого массива типа long double. Если результат операции new[] нe нулевой, указателю 1ptr будет присвоено значение адреса зарезервированной памяти с выводом надписи " Готово!" и последующим высвобождением области. В случае неудачного выделения выводится сообщение " Неудача.. .".

Соседние файлы в папке ЯзыкС++Глушаков