- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Linux программирование в примерах - Арнольд Роббинс
Шрифт:
Интервал:
Закладка:
3.2.1.3. Освобождение памяти: free()
Когда вы завершили использование памяти, «верните ее обратно», используя функцию free(). Единственный аргумент является указателем, предварительно полученным с использованием другой функции выделения. Можно (хотя это бесполезно) передать функции free() пустой указатель:
free(coordinates);
coordinates = NULL; /* не требуется, но хорошая мысль */
После вызова free(coordinates) доступ к памяти, на которую указывает coordinates, запрещен. Она теперь «принадлежит» процедурам выделения, и они могут поступать с ней как сочтут нужным. Они могут изменить содержимое памяти или даже удалить ее из адресного пространства процесса! Таким образом, есть несколько типичных ошибок, которых нужно остерегаться при использовании free():
Доступ к освобожденной памяти
Если она не была освобождена, переменная coordinates продолжает указывать на блок памяти, который больше не принадлежит приложению. Это называется зависшим указателем (dangling pointer). На многих системах вы можете уйти от наказания, продолжая использовать эту память, по крайней мере до следующего выделения или освобождения памяти. На других системах, однако, такой доступ не будет работать. В общем, доступ к освобожденной памяти является плохой мыслью: это непереносимо и ненадежно, и GNU Coding Standards отвергает его. По этой причине неплохо сразу же установить в указателе программы значение NULL. Если затем вы случайно попытаетесь получить доступ к освобожденной памяти, программа немедленно завершится с ошибкой нарушения сегментации (надеемся, до того, как вы успели вашу программу выпустить в свет).
Освобождение одного и того же указателя дважды
Это создает «неопределенное поведение». После передачи блока памяти обратно выделяющим процедурам они могут объединить освобожденный блок с другой свободной памятью, которая есть в их распоряжении. Освобождение чего-то уже освобожденного ведет к неразберихе и в лучшем случае к крушению; известно, что так называемые двойные освобождения приводили к проблемам безопасности.
Передача указателя, полученного не от функций malloc(), calloc() или realloc()
Это кажется очевидным, но тем не менее важно. Плоха даже передача указателя на адрес где-то в середине динамически выделенной памяти:
free(coordinates + 10);
/* Освободить все кроме первых 10 элементов */
Этот вызов не будет работать и, возможно, приведет к пагубным последствиям, таким как крушение. (Это происходит потому, что во многих реализациях malloc() «учетная» информация хранится перед возвращенными данными. Когда free() пытается использовать эту информацию, она обнаружит там недействительные данные. В других реализациях, где учетная информация хранится в конце выделенного блока; возникают те же проблемы.)
Выход за пределы буфера
Доступ к памяти за пределами выделенного блока также ведет к неопределенному поведению, опять из-за того, что она может содержать учетную информацию или, возможно, вообще не принадлежать адресному пространству процесса. Запись в такой участок памяти гораздо хуже, поскольку это может уничтожить учетные данные.
Отказ в освобождении памяти
Любая динамическая память, которая больше не нужна, должна быть освобождена. В частности, необходимо тщательно управлять памятью и освобождать ее, когда она выделяется внутри циклов или рекурсивных или глубоко вложенных вызовов функций. Отказ от этого ведет к утечкам памяти, при которых память процесса может неограниченно расти; в конце концов, процесс завершается из-за нехватки памяти. Эта ситуация может быть особенно разрушительной, если память выделяется для ввода записи или как-то еще связана с вводом: утечка памяти будет незаметна при использовании незначительных объемов ввода, но внезапно станет очевидной (и приведет в замешательство) при больших. Эта ошибка еще хуже для систем, которые должны работать непрерывно, как в системах телефонных коммутаторов. Утечка памяти, вызывающая крушение такой системы, может привести к значительным денежным или другим потерям.
Даже если программа никогда не завершается из-за недостатка памяти, постоянно увеличивающиеся программы теряют производительность, поскольку операционная система должна сохранять использующиеся данные в физической памяти. В худшем случае, это может привести к поведению, известному как пробуксовка (thrashing), при которой операционная система так занята перекачкой содержимого адресного пространства в и из физической памяти, что реальная работа не делается.
Хотя free() может вернуть освобожденную память системе и сократить адресное пространство процесса, это почти никогда не делается. Вместо этого освобожденная память готова для нового выделения при следующем вызове malloc(), calloc() или realloc().
При условии, что освобожденная память продолжает оставаться в адресном пространстве процесса, стоит обнулить ее перед освобождением. Например, такой способ может оказаться предпочтительным для программ с повышенными требованиями к безопасности.
Обсуждение ряда полезных инструментов для отладки динамической памяти см в разделе 15.5.2 «Отладчики выделения памяти».
3.2.1.4. Изменение размера: realloc()
Динамическая память имеет существенное преимущество перед статически объявленными массивами, поскольку это позволяет использовать столько памяти, сколько нужно, и не больше. Не нужно объявлять глобальный, статический или локальный массив фиксированного размера и надеяться, что он: (а) достаточно большой и (б) не слишком большой. Вместо этого можно выделить ровно столько, сколько нужно, не больше и не меньше.
Вдобавок, можно изменять размер динамически выделенной области памяти. Хотя можно сократить размер блока памяти, обычно его увеличивают. Изменение размера осуществляется с помощью realloc(). Продолжая пример с coordinates, типичный код выглядит следующим образом:
int new_count;
size_t new_amount;
struct coord *newcoords; /* установить, например: */
new_count = count * 2; /* удвоить размер памяти */
new_amount = new_count * sizeof(struct coord);
newcoords =
(struct coord*)realloc(coordinates, new_amount);
if (newcoords == NULL) {
/* сообщить об ошибке, восстановить или прервать */
}
coordinates = newcoords;
/* продолжить использование coordinates ... */
Как и в случае с malloc(), шаги стереотипны по природе и сходны по идее.
1. Вычислить новый выделяемый размер в байтах.
2. Вызвать realloc() с оригинальным указателем, полученным от malloc() (или от calloc() или предыдущего вызова realloc()) и с новым размером.
3. Привести тип и присвоить возвращенное realloc() значение. Подробнее обсудим дальше.
4. Как и для malloc(), проверить возвращенное значение, чтобы убедиться, что оно не равно NULL. Вызов любой функции выделения памяти может завершиться неудачей.
При увеличении размера блока памяти realloc() часто выделяет новый блок нужного размера, копирует данные из старого блока в новый и возвращает указатель уже на новый блок. При сокращении размера блока данных realloc() часто обновляет внутреннюю учетную информацию и возвращает тот же указатель. Это избавляет от необходимости копировать первоначальные данные. Однако, если это случится, не думайте, что можно использовать память за пределами нового размера!
В любом случае вы можете предположить, что если realloc() не возвращает NULL, старые данные были скопированы для вас в новый участок памяти. Более того, старый указатель больше недействителен, как если бы вы вызвали free() с ним, и использовать его больше не следует. Это верно для всех указателей на этот блок данных, а не только для того, который использовался при вызове free().
Возможно, вы заметили, что в нашем примере для указания на измененный блок памяти использовалась отдельная переменная. Можно было бы (хотя это плохая идея) использовать ту же самую переменную, как здесь:
coordinates = realloc(coordinates, new_amount);
Это плохо по следующей причине. Когда realloc() возвращает NULL, первоначальный указатель все еще действителен; можно безопасно продолжить использовать эту память. Но если вы повторно используете ту же самую переменную и realloc() возвращает NULL, вы теряете указатель на первоначальную память. Эту память больше нельзя использовать. Что еще важнее, эту память невозможно освободить! Это создает утечку памяти, которую нужно избежать.

