Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Название:Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:0101
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015 краткое содержание
Язык программирования C. Лекции и упражнения (6-е изд.) 2015 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Когда функция gobble() вызывается во второй раз, она снова создает temp и вызывает malloc() для выделения 16 000 байтов памяти. Первый блок из 16 000 байтов недоступен, поэтому функция malloc() должна найти второй блок размером 16 000 байтов. Когда функция завершится, этот блок памяти также становится недоступным и не может использоваться повторно.
Цикл повторяется 1000 раз, и ко времени его завершения из пула памяти будет изъято 16 000 000 байтов. На самом деле программе может просто не хватить памяти, чтобы зайти настолько далеко. Проблема подобного рода называется утечкой памяти и может быть предотвращена за счет наличия вызова free() в конце функции.
514 Глава 12
ФУНКЦИЯ calloc()
Еще один способ выделения памяти предусматривает применение функции calloc(). Ниже показан типичный случай ее использования:
long * newmem;
newmem = (long *) calloc(100, sizeof (long));
Подобно malloc(), функция calloc ( ) возвращает указатель на char в своей версии до выхода стандарта ANSI и указатель па void в условиях действия стандарта ANSI. Если необходим другой тип, вы должны применять приведение. Эта новая функция принимает два аргумента, которые оба должны быть целыми числами без знака (типа size_t в ANSI). Первый аргумент задает желаемое количество ячеек памяти. Второй аргумент задает размер каждой ячейки в байтах. В нашем случае тип long использует 4 байта, поэтому показанный выше оператор устанавливает 100 единиц по 4 байта, задействовав в общей сложности 400 байтов для хранения данных.
Применение sizeof (long) вместо 4 делает код более переносимым. Он будет работать в системах, где тип long имеет размер, отличающийся от 4.
Функция calloc() обладает еще одним свойством: она устанавливает в 0 все биты в блоке. (Однако следует отметить, что в некоторых аппаратных системах значение с плавающей запятой 0 не представляется всеми битами, установленными в 0.)
Функция free() может также использоваться для освобождения памяти, выделенной с помощью calloc().
Динамическое распределение памяти является ключевым средством во многих развитых технологиях программирования. Мы исследуем некоторые из них в главе 17. Вполне вероятно, что ваша библиотека С предлагает ряд других функций управления памятью, при этом часть из них переносима, а часть — нет. Уделите время, чтобы взглянуть на них.
Динамическое распределение памяти и массивы переменной длины
Функциональность массивов переменной длины и malloc() кое в чем пересекается. Например, оба средства могут применяться для создания массива, размер которого определяется во время выполнения:
int vlamal()
{
int n; int * pi;
scanf("%d", &n);
pi = (int *) malloc (n * sizeof(int)); int ar[n]; // массив переменной длины
pi [2] = ar [2] = -5;
} ' "
Одно из отличий между ними заключается в том, что массив переменной длины является автоматической памятью. Следствие использования автоматической памяти состоит в том, что пространство памяти, занимаемое массивом переменной длины, освобождается автоматически, когда поток управления покидает блок, в котором массив определен — в этом случае при завершении функции vlamal(). Таким образом, вы не должны переживать о вызове free(). С другой стороны, доступ к массиву, созданному с применением malloc(), не ограничивается одной функцией.
Классы хранения, связывание и управление памятью 515
Например, одна функция может создать массив и возвратить указатель, предоставляя доступ к нему вызывающей функции. Завершив работу с массивом, вызывающая функция может вызвать free(). Не будет ошибкой, если при вызове free() задать указатель, отличающийся от используемого в вызове malloc(); нужно только, чтобы указатели содержали один и тот же адрес. Однако вы не должны пытаться освободить тот же самый блок памяти дважды.
Массивы переменной длины более удобны для организации многомерных массивов. Вы можете создать двумерный массив с применением malloc(), но синтаксис будет довольно неуклюжим. Если компилятор не поддерживает средство массивов переменной длины, одна из размерностей должна быть зафиксирована, как в вызовах функции:
int n = 5; int m = 6;
int ar2[n][m]; // массив переменной длины n х m
int (* р2)[6]; // работает до выхода стандарта С99
int (* рЗ)[m]; // требуется поддержка массивов переменной длины
р2 = (int (*) [6] ) malloctn * 6 * sizeof(int)); // массив n * 6
рЗ = (int (*)[m]) malloctn * m * sizeof(int)); // массив n * m
// предыдущее выражение также требует поддержки массивов переменной длины
ar2 [1] [2] = р2[1] [2] =12;
Полезно взглянуть на объявления указателей. Функция malloc() возвращает указатель, так что р2 должен быть указателем подходящего типа. Объявление
int (* р2) [6]; // работает до выхода стандарта С99
говорит о том, что р2 указывает на массив из шести элементов int. Это значит, что р2 [i] будет интерпретироваться как элемент, состоящий из шести значений int, а р2 [i] [j] — это одиночное значение int.
Во втором объявлении указателя используется переменная для сообщения размера массива, на который ссылается рЗ. Это означает, что рЗ трактуется как указатель на массив переменной длины, и именно потому данный код не будет работать в рамках стандарта С90.
Классы хранения и динамическое распределение памяти
Вас может интересовать, какова связь между классами хранения и динамическим распределением памяти. Давайте посмотрим на идеализированную модель. Вы можете думать о доступной памяти программы как об имеющей три отдельных области: одна для статических переменных с внешним связыванием, внутренним связыванием и без связывания; одна для автоматических переменных; и одна для динамически выделяемой памяти.
Объем памяти, необходимый для классов со статической продолжительностью хранения, известен на этапе компиляции, и данные, которые хранятся в этой области, доступны на всем протяжении выполнения программы. Каждая переменная этих классов появляется, когда программа запускается, и исчезает при ее завершении.
Однако автоматическая переменная начинает существовать, когда поток управления входит в блок кода, содержащий определение переменной, и исчезает после покидания этого блока. Следовательно, по мере вызова программой функций и их завершения, объем памяти, задействованный под автоматические переменные, возрастает и уменьшается. Такая область памяти обычно реализована в виде стека. Это значит, что новые переменные добавляются в память последовательно, в порядке их создания, а удаляются в обратном порядке, когда исчезают.
516 глава 12
Динамически выделяемая память появляется при вызове malloc() или родственной ей функции и освобождается при вызове free(). Постоянство памяти управляется программистом, а не каким-то набором жестких правил, поэтому блок памяти может быть создан в одной функции и освобожден в другой. По этой причине область памяти, применяемая для динамического распределения памяти, может стать фрагментированной, т.е. неиспользованные участки будут идти вперемешку с активными блоками памяти. Кроме того, использование динамической памяти имеет тенденцию быть более медленным процессом, чем работа со стековой памятью.
Читать дальшеИнтервал:
Закладка: