Правила записи программы на языке Си 5 Правила формального описания синтаксиса языка программирования 6

Вид материалаЛекции

Содержание


10.Работа с динамической памятью 10.1.Стандартные функции управления динамической памятью
10.2.Пример использования динамической памяти
Подобный материал:
1   ...   16   17   18   19   20   21   22   23   ...   28

10.Работа с динамической памятью

10.1.Стандартные функции управления динамической памятью


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

Для запроса динамической памяти служит функция malloc(), которая имеет следующий прототип:


void * malloc(size_t size);


Функция malloc() выделяет область динамической памяти, размером size байт, и возвращает адрес этой области памяти.

Параметр size, имеет тип size_t, который описан в файле с помощью оператора typedef и используется для описания размеров, счетчиков и т.д. Обычно тип size_t соответствует типу unsigned int.

В том случае, когда функция malloc() не может удовлетворить запрос на память, она возвращает значение NULL, то есть значение не существующего указателя. Константа NULL описана в заголовочном файле . Значение NULL возвращается и в том случае, когда значение параметра size нулевое.

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

После того, как выполнена вся работа с выделенной областью памяти, ее следует освободить с помощью функции free(), имеющей следующий прототип:


void free(void *block);


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

Если при вызове функции free() значение указателя block не соответствует адресу, возвращенному функцией выделения памяти, то результат выполнения функции free() непредсказуем, а область динамической памяти может быть вообще разрушена.

Не допускается также освобождать уже освобожденный блок памяти.

Значение параметра block равное NULL не вызывает никаких действий со стороны функции free();

Рассмотрим типичную последовательность действий при работе с динамической памятью:


double *A; int n;

...

n = 200;

...

A = (double *) malloc( n * sizeof(double) );

...

/* Работа с массивом A */

...

free(A);


В рассмотренном фрагменте программы выделяется память для хранения n элементов типа double. В целях совместимости никогда не следует явно задавать размер элемента данных. Нужно пользоваться операцией sizeof(). Возвращаемое функцией malloc() значение преобразуется к типу указателя на double.

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

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


void * calloc(size_t nitems, size_t size);


Функция выделяет непрерывный блок памяти для nitems элементов данных размером size байт каждый и заполняет этот блок нулевыми значениями. В остальном работа ее аналогична работе функции malloc().

Функция realloc() служит для изменения размера ранее выделенного блока памяти:


void *realloc(void *block, size_t size);


Здесь block - адрес ранее выделенного блока памяти, size - новый размер блока в байтах. Функция возвращает значение нового указателя на блок памяти, которое может и не совпадать со старым.

Функция гарантирует сохранность данных в блоке, разумеется, сохранность не более size байт. В остальном работа функции совпадает с работой ранее рассмотренных функций выделения памяти.

Все рассмотренные функции могут выделять память размером не более одного сегмента, то есть не более 64K в 16-ти разрядных моделях и не более 4G в 32-х разрядных моделях памяти.

При работе с динамической памятью следует иметь в виду, что в каждом выделенном блоке несколько байт отводится на служебную информацию. Так в 16-ти разрядной Large модели память выделяется блоками по размеру кратными 16 байтам, и в каждом блоке 4 байта служебные.

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

Функция coreleft() возвращает значение оставшейся в динамической области памяти в байтах. Функция может иметь следующие прототипы в зависимости от моделей памяти:


unsigned coreleft(void); /* Маленьких модели */

unsigned long coreleft(void); /* Большие модели */


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

10.2.Пример использования динамической памяти


В следующем примере динамическая память используется для хранения элементов массива заранее неопределенного размера, ввод которого прекращается при появлении во входном потоке числа больше или равного 1e300. При этом, вначале под массив выделяется сегмент памяти, максимального размера, затем размер блока памяти устанавливается соответствующим фактическому размеру массива.


#include

#include

#include

#include

#include


void * Malloc( size_t size )

{

void *p = malloc(size);

if( !p )

{ printf("Недостаточно памяти!\n"); exit(1); }

return p;

}


void * Realloc ( void *block, size_t size )

{

void *p = realloc(block, size);

if( !p ) { printf("Недостаточно памяти!\n"); exit(1); }

return p;

}


void main(void)

{

double *A, temp;

unsigned i, n, maxN, goodIO;

A = (double *) Malloc( maxN = UINT_MAX );

maxN /= sizeof(double);

for(goodIO = n = 0; n < maxN; n++)

{

printf("A[%d] = ", n); scanf("%lf", &temp);

if(temp >= 1e300) { goodIO = 1; break; }

A[n] = temp;

}

if(goodIO)

{

A = (double *) Realloc(A, n * sizeof(double));

/* Обработка массива. Для примера - печать. */

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

{

printf("%10.3lf ", A[i]);

if( (i + 6) % 5 == 0 ) printf("\n");

if( (i + 121) % 120 == 0 ) { getch(); clrscr(); }

}

printf("\n");

}

free(A);

}


Максимальный размер сегмента в байтах всегда равен величине наибольшего беззнакового целого числа, значение которого определяет константа UINT_MAX из заголовочного файла .

В программе используются вспомогательные функции Malloc() и Realloc() для обеспечения контроля выделения памяти. В них функция exit() с прототипом в файле используется для прерывания работы программы.