
Основы алгоритмизации (ОАП) / Теория / Дин_матрицы
.pdf
Главная
ДИНАМИЧЕСКИЕ ОБЪЕКТЫ
Во многих случая во время выполнения программы у компьютера есть неиспользуемая память. Эта память называется «кучей» или «свободным хранилищем». Внутри этого «хранилища» можно выделить память, и затем освободить выделенное пространство и вернуть его в «свободное хранилище», после того, как необходимость в переменных отпадет. Эта техника позволяет эффективно расходовать память и разрабатывать программы, способные решать масштабные проблемы, обрабатывая большие объемы данных.
Также, при традиционном определении массива:
тип имя_массива [количество_элементов];
общее количество памяти, выделяемой под массив, задается определением и равно sizeof имя_массива. Но иногда бывает нужно, чтобы память под массив выделялась для решения конкретной задачи, причем ее размеры заранее не известны и не могут быть фиксированы.
Формирование объектов с переменными размерами можно организовать с помощью указателей и средств динамического распределения памяти двумя способами: с использованием библиотечных функций (С); с использованием операций
new и delete (С++).
Формирование динамических переменных с использованием библиотечных функций С
При программировании на C++, можно столкнуться с большим числом программ, полученных в наследство от языка С. Поэтому необходимо дополнительно обсудить динамическое выделение памяти в стиле С.
Для выделения и освобождения динамической памяти используются функции, приведенные в табл. 1.
Для выделения памяти используется функция malloc, параметром которой является размер выделяемого участка памяти, равный n*sizeof(int). Так как функция malloc возвращает нетипизированный указатель void*, то необходимо выполнить преобразование полученного нетипизированного указателя в необходимый тип-указатель. Освободить выделенную память можно функцией free():
1

//Функция для формирования одномерного
//динамического массива
#include <malloc.h> #include <stdlib.h>
int * make_arr(int n)
{
int *arr;
arr = (int*)malloc(n*sizeof(int));
for(int i = 0; i < n ; i++) arr[i] = rand()% 10; return arr;
}
Таблица 1
|
Функции работы с динамической памятью |
|
|
Функция |
Прототип и краткое описание |
malloc |
void * malloc(unsigned s) |
|
Возвращает указатель на начало области динамической |
|
памяти длиной в s байт, при неудачном завершении |
|
возвращает NULL |
calloc |
void * calloc(unsigned n, unsigned m) |
|
Возвращает указатель на начало области динамической |
|
памяти для размещения n элементов длиной по m байт |
|
каждый, при неудачном завершении возвращает NULL |
realloc |
void * realloc(void * p, unsigned s) |
|
Изменяет размер блока ранее выделенной динамической |
|
памяти до размера s байт, р-адрес начала изменяемого |
|
блока, при неудачном завершении возвращает NULL |
free |
void *free(void p) |
|
Освобождает ранее выделенный участок динамической |
|
памяти, р-адрес первого байта |
Программы на C++ могут поддерживать память, выделяемую с помощью malloc и уничтожаемую с помощью free. Однако, лучше использовать операторы new и
delete.
Формирование динамических переменных с использованием операций new и delete
Для динамического распределения памяти в C++ используются операции new и
delete. Формат: new имя_типа;
или
2

new имя_типа [инициализатор];
позволяет выделить и сделать доступным свободный участок памяти, размеры которого соответствуют типу данных, определяемому именем типа. В выделенный участок заносится значение, определяемое инициализатором, который не является обязательным параметром. В случае успешного выделения памяти операция возвращает адрес начала выделенного участка памяти, если участок не может быть выделен, то возвращается NULL и программа может быть прервана по исключению. Например:
int *i;
i = new int (10);
//создать переменную типа int,инициализировать - 10
...
float *f;
f = new float;
//создать динамическую переменную типа float int *mas = new[5];
// создать динамический массив из 5 целых элемен.
Операция delete освобождает участок памяти, ранее выделенный операцией new.
delete i; //свободить память по указателю i
...
delete f; // свободить память по указателю f delet [] mas;
// свободить память выделенную под массив mas
Динамическое выделение памяти – потенциальный источник ошибок, и возможно, наиболее распространенная из них в этом контексте – утечка памяти. Это происходит, когда используется операция new для выделения памяти, но не используется операция delete для ее освобождения, когда она больше не нужна.
По отношению к массивам динамическое распределение памяти особенно полезно, поскольку иногда бывают массивы больших размеров.
При формировании динамической матрицы сначала выделяется память для массива указателей на одномерные массивы, а затем в цикле с параметром выделяется память под n одномерных массивов (рис.1). Пример:
3

//Функция для формирования двумерного
//динамического массива
int ** make_matr(int n)
{
int **matr; // адрес адресов – массив указателей int i,j;
matr = new int*[n];
// выделить память под массив из n элементов for (i = 0; i < n ; i++)
{
matr[i] = new int[n];
//выделить память n раз под массив из n
for (j = 0; j < n ;j++)
matr[i][j] = rand()% 10; // инициализация
}
return matr;
}
**matr
*matr[1] *matr[2] *matr[3] . . . . *matr[n]
Рис. 1. Схема выделения памяти под n одномерных массивов
Нельзя специфицировать начальные значения элементов массива, который распределен динамически. Это следует сделать явным присвоением. Чтобы освободить память, необходимо выполнить цикл для освобождения одномерных массивов:
for(int i = 0; i < n ;i++) delete matr[i];
//После этого освобождаем память на которую //указывает указатель matr
delete [] matr;
Для уничтожения динамического массива применяется оператор delete c []. Скобки ([]) играют важную роль. Они сообщают оператору, что требуется уничтожить все элементы массива, а не только первый.
4

Константный динамический объект
Чтобы создать динамический объект и запретить изменение его значения после инициализации? необходимо объявить объект константным. Для этого применяется следующая форма оператора new:
const int *pci = new const int(1024);
Константный динамический объект имеет несколько особенностей. Во-первых, он должен быть инициализирован, иначе компилятор сигнализирует об ошибке (кроме случая, когда объект принадлежит к типу класса, имеющего конструктор по умолчанию; в такой ситуации инициализатор можно опустить). Во-вторых, указатель, возвращаемый выражением new, должен адресовать константу. В предыдущем примере pci служит указателем на const int. Константность динамически созданного объекта подразумевает, что значение, полученное при инициализации, в дальнейшем не может быть изменено. Временем его жизни управляет оператор delete.
Смешивание способов динамического распределения памяти в стиле new-delete со стилем malloc-free является логической ошибкой: пространство, созданное с помощью malloc, не может быть освобождено с помощью delete; объекты, созданные с помощью new, не могут быть уничтожены с помощью free.
Массивы указателей
Массив указателей является самой простой и одновременно самой распространенной динамической структурой данных. Одно из его определений имеет вид:
double *p[20];
В соответствии с принципом контекстного определения типа данных переменную p следует понимать как массив, каждым элементом которого является указатель на переменную типа double. Исходя из принятой концепции указателя, эту структуру можно рассматривать как массив указателей на отдельные переменные типа double, так и на массивы этих переменных:
char *pc[] = { "aaa", "bbb", "ccc", NULL};
Массивы указателей? как и все остальные структуры данных, содержащие указатели, допускают различные способы формирования, которые отличаются как способом создания самих элементов, так и способом установления связей между ними. Например:
5

double a1,a2,a3, *pd[] = { &a1, &a2, &a3, NULL};
// переменные - статические, указатели инициализируются double d[19], *pd[20];
.......................
for (i = 0; i < 19; i++) pd[i] = &d[i];
pd[i] = NULL;
......................
double *p, *pd[20];
for (i = 0; i < 19; i++)
{
p = new double; // переменные создаются динамическ
*p = i;
pd[i] = p; // массив указателей -статически:
}
pd[i] = NULL;
......................
double **pp, *p;
pp= new double *[20];
//массив указателей создается динамически
for (i = 0; i < 19; i++)
{
p = new double;
// переменные создаются динамически
*p = i; pp[i] = p;
}
pp[i] = NULL;
6