
ЛАБОРАТОРНАЯ РАБОТА №6
«Линейные однонаправленные списки»
1. Цель работы
Целью работы является изучение работы с однонаправленными списками и выполнением простейших операций с элементами списка, размещенных в динамической памяти.
2. Содержание работы
Для выполнения лабораторной работы необходимо:
Изучить принципы работы с динамической памятью.
Изучить способы проектирования линейных списков.
Спроектировать и отладить программу решения поставленной задачи.
Составить отчет.
Защитить лабораторную работу, представив отчет, работающий программный продукт и отвечая на вопросы преподавателя, как теоретического, так и практического характера.
3. Теоретические сведения
3.1.Работа с динамической памятью в с
В предыдущих работах мы рассматривали программирование, связанное с обработкой только статических данных. Статическими величинами называются такие, память под которые выделяется во время компиляции и сохраняется в течение всей работы программы.
В языке программирования C существует и другой способ выделения памяти под данные, который называется динамическим. В этом случае память под величины отводится во время выполнения программы. Такие величины будем называть динамическими. Раздел оперативной памяти, распределяемый статически, называется статической памятью; динамически распределяемый раздел памяти называется динамической памятью (динамически распределяемой памятью).
При этом способе память распределяется для информации из свободной области памяти по мере необходимости. Область свободной памяти лежит между вашей программой с ее постоянной областью памяти и стеком. Динамическое размещение удобно, когда неизвестно, сколько элементов данных будет обрабатываться. Предложенный ANSI стандарт определяет, что информация, необходимая системе динамического распределения, будет находиться в stdlib.h. Однако, С помещает также информацию заголовка распределения в alloc.h.
Таким образом, использование динамических величин предоставляет программисту ряд дополнительных возможностей.
Во-первых, подключение динамической памяти позволяет увеличить объем обрабатываемых данных.
Во-вторых, если потребность в каких-то данных отпала до окончания программы, то занятую ими память можно освободить для другой информации.
В-третьих, использование динамической памяти позволяет создавать структуры данных переменного размера.
Распределение памяти представлено на рис.1.
Память системы
Высший адрес
Стековая область
Область свободной памяти для динамического размещения
Область глобальных переменных
Область программы
Низший адрес
Рис 1. Распределение оперативной памяти для программ на С.
3.2. Функции, работающие с динамической памятью
Работа с динамическими величинами связана с использованием еще одного типа данных — ссылочного типа. Величины, имеющие ссылочный тип, называют указателями.
Указатель содержит адрес поля в динамической памяти, хранящего величину определенного типа. Сам указатель располагается в статической памяти.
Адрес величины — это номер первого байта поля памяти, в котором располагается величина. Размер поля однозначно определяется типом.
По запросам функции malloc() память выделяется из свободной области, которая располагается непосредственно над областью глобальных переменных, причем адресация растет в сторону стековой области. Таким образом, в экстремальных случаях стековая область может наложиться на область динамического размещения.
Функции, работающие с динамической памятью, используются для создания динамических переменных и массивов произвольных типов. Единственно важным для них является размерность структур данных, выраженная естественным для С способом в байтах (при помощи операции sizeof).
Адрес выделенной области памяти также возвращается в виде указателя типа void* - абстрактный адрес памяти без определения адресуемого типа данных. Рассмотрим объявления основных функций:
void *malloc(int size);
// выделить область памяти размером
// в size байтов и возвратить адрес
void *calloc(n, sizeof(objеct));
// выделить область памяти, достаточной для размещения
// n объектов указанного размера
void free(void *p);
// освободить область памяти,
// выделенную по адресу p
void *realloc(void *p, int size);
// расширить выделенную область памяти
// до размера size, при изменении адреса
// переписать старое содержимое блока
Пример:
Создание простой динамической переменной:
double *pd;
pd = malloc(sizeof(double));
if (pd !=NULL)
{
*pd = 5;
...
free(pd);
}