- •1 Понятие алгоритма формы представления алгоритмов
- •2 Линейные и разветвляющиеся алгоритмы.
- •3 Циклические алгоритмы
- •4 Понятие прикладного и системного программирования
- •5 Структура программ на языке си
- •6 Арифметико-логические операции над переменными
- •Арифметические операции
- •Операции увеличения и уменьшения
- •Операции "увеличить на", "домножить на" и т.П.
- •Логические операции
- •Операции сравнения
- •Побитовые логические операции
- •7 Два вида оператора выбора Оператор if
- •8 Понятие цикла
- •1.4.6. Оператор break
- •1.4.7. Оператор for
- •1.4.8. Оператор while
- •1.4.9. Оператор do while
- •1.4.10. Оператор continue
- •1.4.11. Оператор return
- •9 Массив
- •12 Указатели
- •13 Адресная арифметика
- •14 Операции над указателями
- •15 Массивы указателей
- •16 Указатели на функции Указатели на функции
- •17 Структуры
- •18 Доступ к элементам структуры
- •19 Структуры как аргументы
- •6.2. Структуры и функции
- •20 Динамические структуры на массиве
- •21 Динамическое распределение памяти Функции динамического распределения
- •Динамическое выделение памяти для массивов
- •23 Организация доступа к файлам
- •Закрытие потока при помощи fclose
- •Чтение из потока при помощи fgetc
- •«Ловушка» eof
- •При помощи fgets
- •24 Форматированный ввод –вывод Функции форматированного ввода и вывода в си
- •Спецификатор типа
- •Спецификатор типа
- •25 Верификация тестирование отладка программ
- •Введение
- •26 Причины и последствия появления ошибок
- •27 Идея модульного программирования
- •28 Технология проектирования сверху-вниз
- •29 Итерация рекурсия
- •30 Методы сортировки
- •34 Способы защиты информации
21 Динамическое распределение памяти Функции динамического распределения
Указатели используются для динамического выделения памяти компьютера для хранения данных. Динамическое распределение означает, что программа выделяет память для данных во время своего выполнения. Память для глобальных переменных выделяется во время компиляции, а для нестатических локальных переменных — в стеке. Во время выполнения программы ни глобальным, ни локальным переменным не может быть выделена дополнительная память. Но довольно часто такая необходимость возникает, причем объем требуемой памяти заранее неизвестен. Такое случается, например, при использовании динамических структур данных, таких как связные списки или двоичные деревья. Такие структуры данных при выполнении программы расширяются или сокращаются по мере необходимости. Для реализации таких структур в программе нужны средства, способные по мере необходимости выделять и освобождать для них память.
Память, выделяемая в С функциями динамического распределения данных, находится в т.н. динамически распределяемой области памяти (heap)[1]. Динамически распределяемая область памяти — это свободная область памяти, не используемая программой, операционной системой или другими программами. Размер динамически распределяемой области памяти заранее неизвестен, но как правило в ней достаточно памяти для размещения данных программы. Большинство компиляторов поддерживают библиотечные функции, позволяющие получить текущий размер динамически распределяемой области памяти, однако эти функции не определены в Стандарте С. Хотя размер динамически распределяемой области памяти очень большой, все же она конечна и может быть исчерпана.
Основу системы динамического распределения в С составляют функции malloc() и free(). Эти функции работают совместно. Функция malloc() выделяет память, а free() — освобождает ее. Это значит, что при каждом запросе функция malloc() выделяет требуемый участок свободной памяти, a free() освобождает его, то есть возвращает системе. В программу, использующую эти функции, должен быть включен заголовочный файл <stdlib.h>.
Прототип функции malloc() следующий:
void *malloc(size_t количество_байтов);
Здесь количество_байтов — размер памяти, необходимой для размещения данных. (Тип size_t определен в <stdlib.h> как некоторый целый без знака.) Функция malloc() возвращает указатель типа void *, поэтому его можно присвоить указателю любого типа. При успешном выполнении malloc() возвращает указатель на первый байт непрерывного участка памяти, выделенного в динамически распределяемой области памяти. Если в динамически распределяемой области памяти недостаточно свободной памяти для выполнения запроса, то память не выделяется и malloc() возвращает нуль.
При выполнении следующего фрагмента программы выделяется непрерывный участок памяти объемом 1000 байтов:
char *p;
p = malloc(1000); /* выделение 1000 байтов */
После присвоения указатель p ссылается на первый из 1000 байтов выделенного участка памяти.
В следующем примере выделяется память для 50 целых. Для повышения мобильности (переносимости программы с одной машины на другую) используется оператор sizeof.
int *p;
p = malloc(50*sizeof(int));
Поскольку динамически распределяемая область памяти не бесконечна, при каждом размещении данных необходимо проверять, состоялось ли оно. Если malloc() не смогла по какой-либо причине выделить требуемый участок памяти, то она возвращает нуль. В следующем примере показано, как выполняется проверка успешности размещения:
p = malloc(100);
if(!p) {
printf("Нехватка памяти.\n");
exit(1);
}
Конечно, вместо выхода из программы exit() можно поставить какой-либо обработчик ошибки. Обязательным здесь можно назвать лишь требование не использовать указатель р, если он равен нулю.
Функция free() противоположна функции malloc() в том смысле, что она возвращает системе участок памяти, выделенный ранее с помощью функции malloc(). Иными словами, она освобождает участок памяти, который может быть вновь использован функцией malloc(). Функция free() имеет следующий прототип:
void free(void *p)
Здесь р — указатель на участок памяти, выделенный перед этим функцией malloc(). Функцию free() ни в коем случае нельзя вызывать с неправильным аргументом, это мгновенно разрушит всю систему распределения памяти.
Подсистема динамического распределения в С используется совместно с указателями для создания различных программных конструкций, таких как связные списки и двоичные деревья. Несколько примеров использования таких конструкций приведены в части IV. Здесь рассматривается другое важное применение динамического размещения: размещение массивов.