Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

lab_C-13

.doc
Скачиваний:
1
Добавлен:
11.08.2019
Размер:
75.78 Кб
Скачать

Рубанчик В.Б.,

Михайличенко В.Н.

Лабораторная работа "Исследование механизма динамического выделения памяти"

4/4

ЛАБОРАТОРНАЯ РАБОТА

Тема: Исследование механизма динамического выделения памяти

Цель работы: Изучить приемы динамического выделения, перераспределения и освобождения блоков памяти

Функции для управления механизмом динамического выделение памяти

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

Функции, используемые в операциях динамического выделения и освобождения памяти, имеют прототипы в заголовочном файле alloc.h и дублируются (кроме специфической для DOS-версии компилятора BC++ функции coreleft) в stdlib.h.

Детали алгоритмов выделения и освобождения памяти зависят от компилятора. Обычно память выделяется некоторыми порциями. Например, при использовании модели small компилятор BC++ 3.1 предусматривает выделение памяти порциями по 16 байт. Поэтому реально объем выделенной памяти может оказаться больше запрошенного. Кроме того, компиляторы могут отводить несколько дополнительных байт для служебной информации.

1. Функция malloc.

void *malloc(size_t size).

Функция malloc выделяет в куче блок размером size байт. Тип size_t — синоним беззнакового целочисленного типа (часто unsigned int, введенный в стандарт для описания размеров любых объектов данных).

Возвращаемое значение:

— При успешном выделении памяти функция возвращает адрес первого байта выделенного блока памяти (бестиповый указатель!).

— В случае ошибки (например, не хватает места для выделения блока заказанного размера), функция возвращает NULL.

— Если аргумент size равен нулю, функция возвращает NULL.

2. Функция calloc:

void *calloc(size_t nitems, size_t size).

Функция calloc выделяет блок памяти размером nitems * size байт, и заполняет выделенную область нулями.

Возвращаемое значение:

— При успешном выделении памяти функция возвращает указатель на выделенный блок памяти.

— В случае ошибки (не хватает места для выделения блока), функция возвращает NULL.

3. Функция realloc.

void *realloc (void *block, size size).

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

Аргумент block — это адрес блока памяти, выделенного ранее функциями malloc, calloc или realloc. Аргумент size задает размер нового блока.

Возвращаемое значение: при успешном выполнении операции функция возвращает адрес вновь выделенного блока, при неудаче — NULL.

4. Функция free:

void free(void *block);

Функция используется для освобождения блока памяти, ранее выделенного с помощью функций malloc, calloc или realloc.

Аргумент block — это адрес освобождаемого блока. Функция не имеет возвращаемого значения.

5. Функция coreleft (для малых моделей памяти с двухбайтовой адресацией):

unsigned coreleft(void).

Нестандартная функция coreleft поддерживается только компилятором BC++ и только для DOS-программ. Ее прототип определен в заголовочном файле alloc.h.

Функция возвращает размер свободной, и потому доступной для распределения оперативной памяти (размер свободного адресного пространства между вершинами стека и кучи).

Замечание

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

ЗАДАНИЕ 1 (операции выделения и освобождения памяти)

Результаты работы программы занести в отчет.

1. С помощью coreleft получить и вывести на экран объем памяти, доступной для динамического распределения.

2. С помощью функции malloc выделить 100 байт памяти.

3. Вновь с помощью coreleft получить и вывести на экран объем памяти, доступной для динамического распределения.

4. С помощью функции calloc выделить 40 байт памяти (любым способом).

5. С помощью coreleft получить и вывести на экран объем памяти, доступной для динамического распределения.

6. Освободить память, выделенную с помощью calloc, получить и вывести на экран объем доступной памяти .

7. Освободить память, выделенную с помощью malloc, получить и вывести на экран объем доступной памяти.

8. Сделать второй вариант программы, который отличается от первого порядком пунктов 6 и 7, т.е. сначала освобождается память выделенная с помощью malloc, а потом — с помощью calloc.

Что изменилось в результатах и как это можно объяснить?

ЗАДАНИЕ 2 (исследование механизма выделения памяти)

Выполняется вариант задания, указанный преподавателем. Исходные данные для каждого варианта приведены после задания.

Отчет: В отчет включить результаты работы программы по пунктам, с анализом всех полученных результатов.

1. Определить в программе бестиповый указатель ptr, который будет использоваться для хранения адресов, возвращаемых функциями выделения памяти (какого типа должен быть этот указатель?).

2. Вывести доступный объем свободной памяти с поясняющим текстом "До выделения памяти";

3. С помощью функции calloc выделить память для N элементов типа TYPE;

4. Используя ptr в роли имени массива из N элементов типа TYPE, в цикле вывести для каждого элемента его адрес и значение (какими должны быть эти значения?).

5. Вывести объем доступной памяти с поясняющим текстом "После работы calloc", и сравнить его с начальным (результат сравнения вывести на экран).

6. Освободить память, выделенную calloc, и вывести объем свободной памяти с пояснением "После освобождения памяти". Проанализировать полученный результат;

7. C помощью функции malloc выделить память для N значений типа TYPE, сохранив адрес блока в указателе ptr. Рассматривая полученный блок как массив, в цикле присвоить его элементам, произвольные значения в возрастающем порядке (например, 1, 2, …). Вывести адреса и значения элементов, сравнить адреса с полученными после применения calloc;

8. С помощью функции realloc изменить размер блока, выделенного в предыдущем пункте, для N1 значений типа TYPE1. Изменился ли адрес блока?

9. Вывести объем свободной памяти с пояснением "После работы realloc", и проанализировать его величину. Проверить, были ли скопированы в новый блок значения, которые были занесены в старый блок..

Варианты задания:

1. TYPE — int, TYPE1 — long, N=3, N1=5.

2. TYPE — float, TYPE1 — int, N=4, N1=7.

3. TYPE — int, TYPE1 — double, N=4, N1=6.

4. TYPE — long, TYPE1 — float, N=2, N1=5.

5. TYPE — float, TYPE1 — double, N=4, N1=7.

6. TYPE — double, TYPE1 — float, N=2, N1=9.

7. TYPE — long, TYPE1 — int, N=3, N1=8.

8. TYPE — int, TYPE1 — float, N=4 , N1=6.

9. TYPE — double, TYPE1 — int, N=5 , N1=8.

10. TYPE — long, TYPE1 — float, N=4 , N1=7.

11. TYPE — float, TYPE1 — long, N=3 , N1=6.

12. TYPE — double, TYPE1 — float, N=4 , N1=7.

13. TYPE — int, TYPE1 — long, N=5 , N1=8.

14. TYPE — float, TYPE1 — int, N=4 , N1=9.

15. TYPE — int, TYPE1 — double, N=3 , N1=7.

16. TYPE — long, TYPE1 — float, N=4 , N1=8.

17. TYPE — float, TYPE1 — double, N=5 , N1=7.

18. TYPE — double, TYPE1 — float, N=3 , N1=6.

19. TYPE — long, TYPE1 — int, N=4 , N1=9.

20. TYPE — int, TYPE1 — float, N=5 , N1=8.

21. TYPE — double, TYPE1 — int, N=3 , N1=9.

22. TYPE — long, TYPE1 — float, N=5 , N1=8.

23. TYPE — float, TYPE1 — long, N=4 , N1=7.

24. TYPE — double, TYPE1 — float, N=3 , N1=9.

Обработка строк с динамическим выделением памяти

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

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

ЗАДАНИЕ 3 (использование механизма выделения памяти для обработки строк)

1. Программа содержит функцию remove_numbers, которая имеет прототип:

char* remove_numbers(char s[])

Функция remove_numbers

а) получает через аргумент s строку символов, из которой нужно удалить цифры,

б) подсчитывает, сколько в строке s символов, не являющихся цифрами,

в) исходя из этого, выделяет память для новой строки,

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

д) возвращает в вызывающую функцию указатель на новую строку.

2. В функции main

а) определяется символьная строка, включающая цифры,

б) вызывается функция remove_numbers,

в) печатается строка, возвращенная функцией remove_numbers,

г) освобождается память, выделенная для новой строки.

Вопросы для контроля

1. В чем различия между функциями malloc и calloc?

2. Доступна ли динамическая память за пределами того блока/функции программы, в котором она была выделена?

3. Что может произойти с программой, если не освобождать динамически выделенную память, когда в ней отпала необходимость?

4. Можно ли изменить размеры динамически выделенного блока памяти? Как это сделать? Если размер изменить можно, что происходит с данными, находящимися в нем?

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]