Размер распределенного блока памяти можно определить, вызвав функцию HeapSize (эту функцию следовало бы назвать BlockSize, поскольку о размере кучи она ничего не сообщает), используя в качестве параметров дескриптор кучи и указатель на блок.
DWORD HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
Возвращаемое значение:в случае успешного выполнения — размер блока; иначе — ноль.
Флаг HEAP_NO_SERIALIZE
При вызове функций HeapCreate, HeapAlloc и HeapReAlloc можно указывать флаг HEAP_NO_SERIALIZE. Использование этого флага иногда обеспечивает незначительный выигрыш в производительности, поскольку во время обращения функции к куче взаимоисключающая блокировка к потокам в этом случае применяться не будет. Результаты простых тестов, в которых не делалось ничего, кроме распределения блоков памяти, показали повышение производительности примерно на 16 процентов. Этот флаг без какого бы то ни было риска можно использовать в следующих ситуациях:
• Программа не использует потоки (глава 7), или, точнее, процесс (глава 6) имеет только один поток. В данной главе этот флаг используется во всех примерах.
• Каждый поток имеет собственную кучу или набор куч, и никакой другой поток не имеет доступа к этой куче.
• Программа располагает собственным механизмом взаимоисключающей блокировки, который предотвращает одновременный доступ к куче сразу нескольких потоков, использующих функции HeapAlloc и HeapAlloc. Для этой цели также могут применяться функции HeapLock и HeapUnlock.
Флаг HEAP_GENERATE_EXCEPTIONS
Разрешение исключений вместо возврата значений NULL в случае сбоев при распределении памяти позволяет избавиться от утомительной необходимости тестирования результатов каждой попытки такого распределения. К тому же, обработчики исключений или завершения могут производить очистку памяти, которая к этому моменту была частично распределена. Эта методика применена в нескольких примерах.
Возможны два кода исключения:
1. STATUS_NO_MEMORY: это значение указывает на то, что системе не удалось создать блок запрошенного объема. Причинами этого могут быть фрагментация памяти, достижение нерастущей кучей максимально допустимого размера или исчерпание всей доступной памяти растущими кучами.
2. STATUS_ACCESS_VIOLATION: это значение указывает на повреждение кучи.
Одной из возможных причин этого может быть выполнение программой записи в память с выходом за границы распределенного блока.
Функция HeapCompact пытается уплотнить, или дефрагментировать, смежные блоки в куче. Функция HeapValidate пытается обнаруживать повреждения кучи. Функция HeapWalk перечисляет блоки в куче, а функция GetProcessHeaps получает все действительные дескрипторы куч.
Функции HeapLock и HeapUnlock позволяют потоки сериализовать доступ к куче, о чем говорится в главе 8.
Имейте в виду, что эти функции не работают под управлением Windows 9x или Windows СЕ. Кроме того, имеются некоторые вышедшие из употребления функции, которые использовались ранее для совместимости с 16-битовыми системами. Мы упомянули об этих функциях лишь для того, чтобы лишний раз подчеркнуть тот факт, что многие функции продолжают поддерживаться, хотя никакой необходимости в них больше нет.
Резюме: управление кучами
Обычная процедура использования куч не представляет никаких сложностей:
1. Получите дескриптор кучи, воспользовавшись одной из функций CreateНеар или GetProcessHeap.
2. Распределите блоки из кучи, используя функцию HeapAlloc.
3. В случае необходимости освободите все или только некоторые блоки при помощи функции HeapFree.
4. Уничтожьте кучу и закройте ее дескриптор при помощи функции HeapDestroy.
Этот процесс иллюстрируют рис. 5.2 и программа 5.2.
В отсутствие необходимости создания отдельных куч или генерации исключений программисты, которые привыкли использовать функции управления памятью из библиотеки С, могут использовать их и далее. При этом, если речь идет о куче процесса, функция malloc эквивалентна функции HeapAlloc, функция realloc — функции HeapReAlloc, а функция free — функции HeapFree. Функция calloc распределяет память и инициализирует объекты, и ее поведение легко эмулируется функцией HeapAlloc. Эквивалент функции HeapSize в библиотеке С отсутствует.
Пример: сортировка файлов с использованием бинарного дерева поиска
Распространенными динамическими структурами данных, требующими управления памятью, являются деревья поиска. Деревья поиска предоставляют удобный способ сопровождения коллекций записей, дополнительным преимуществом которого является возможность применения чрезвычайно эффективных алгоритмов обхода узлов.
Читать дальше