Динамическое распределение памяти используется для выделения памяти для хранения данных в процессе работы программы, когда общий объём данных и сами данные на этапе написания программы не известны, а становятся известны лишь на этапе выполнения программы. Динамическое распределение памяти позволяет получить необходимый объём памяти для ближней или дальней кучи, в зависимости от типа указателя и модели памяти. Для этого существуют специальные библиотечные функции, описанные в файле alloc.h.
Процесс работы с динамической памятью:
- получение динамической памяти заданного объёма;
- работа с данными, распределяемыми в динамической памяти (если необходимо, то возможно перераспределение выделенного объёма памяти с сохранением находящихся в нём данных);
- освобождение динамической памяти по окончании работы с данными.
Для получения блока динамической памяти используются функции malloc() и calloc(). Прототипы функций:
void* malloc(size_t size);
void* calloc(size_t nitems, size_t size);
Функция malloc() в качестве аргумента принимает размер запрашиваемого блока в байтах. Функция calloc() в качестве первого аргумента принимает число элементов, под которые необходимо выделить память, в качестве второго аргумента – размер одного элемента в байтах. Тип size_t аналогичен типу unsigned int. Функции в случае успешного выделения возвращают указатель на тип void, содержащий адрес выделенного блока, который нужно явно преобразовать к указателю на необходимый тип данных. В случае ошибки (как правило связанной с тем, что такого объёма свободной памяти нет) функции возвращают значение NULL. NULL – это стандартная константа языка C, которая обозначает нулевой указатель. После выполнения динамического распределения памяти необходимо обязательно проверять, какое значение возвратили функции. Если значения указателя – NULL, то работа с памятью невозможна (в большинстве случаев следует завершить программу).
Для перераспределения уже выделенного объёма памяти используется функция realloc(). Прототип функции:
void* realloc(void* block, size_t size);
Функция изменяет размер ранее выделенного блока, сохраняя его содержимое. В качестве первого аргумента функция принимает адрес ранее выделенного блока, в качестве второго аргумента – новый размер блока, при этом новый блок может оказаться в другом месте кучи. Функция в случае успешного выделения возвращает указатель на тип void, содержащий адрес нового блока, который нужно явно преобразовать к указателю на необходимый тип данных. Функция возвращает NULL, если размер нового блока больше размера старого блока, и в памяти нет свободного места для размещения нового блока.
Для освобождения блока динамической памяти используется функция free(). Прототип функции:
void free(void* block);
Функция выполняет освобождение блока памяти, адрес которого принимает в качестве аргумента. Все блоки динамической памяти должны быть освобождены по окончании работы с ними, иначе они так и останутся занятыми по окончании работы программы до перезагрузки системы, что приведёт к утечке оперативной памяти.
В среде Borland C++ 3.1 для работы с дальней кучей необходимо использовать дальние указатели и соответствующие функции, но с приставкой far.
Пример 2
#include <stdio.h>
#include <alloc.h>
#include <stdlib.h>
void main(void)
{
int n,m;
printf("Input string number\n");
scanf("%d",&n);
printf("Input column number\n");
scanf("%d",&m);
float *f,sum=0;
if((f=(float*)malloc(n*m*sizeof(float)))==NULL)
{
printf("System do not have free memory\n");
exit(1);
}
printf("Input matrix");
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
printf("\nmatrix[%d][%d]=",i,j);
scanf("%f",(f+i*m+j));
}
if((f=(float*)realloc(f,(n+1)*m*sizeof(float)))==NULL)
{
printf("System do not have free memory\n");
exit(1);
}
for(int t=0;t<m;t++,sum=0)
{
for(int j=0;j<n;j++)
sum+=*(f+j*m+t);
*(f+n*m+t)=sum;
}
for(int k=0;k<n+1;k++)
{
printf("\n");
for(int j=0;j<m;j++)
printf("%f\t",*(f+k*m+j));
}
free(f);
}