Форма отчета по лабораторной работе. Отчет должен содержать: титульный лист; цель работы; условие задачи; текст программы с комментариями; результаты работы программы с контрольным примером
Отчет должен содержать: титульный лист; цель работы; условие задачи; текст программы с комментариями; результаты работы программы с контрольным примером небольшой размерности и ручной расчет контрольного примера для проверки правильности работы алгоритмов; выводы по работе.
Вопросы для самоконтроля
1. Назначение функций в языке Си.
2. Определение, описание и вызов функции.
3. Передача в функцию массивов.
4. Способы передачи параметров в функцию.
5. Вызов функции как «функции» и вызов функции как «процедуры».
Лабораторная работа № 6. Изучение динамических структур данных. Списки
Цель и задачи работы, требования к результатам ее выполнения
Цель работы состоит в овладении навыками разработки программ на языке Си, использующих линейные списки, при работе с данными, имеющими динамическую структуру. Для достижения цели необходимо выполнить следующие задачи:
- изучить учебный материал, посвященный линейным спискам [3, 4];
- разработать программу на языке Си для решения заданного варианта задания;
- отладить программу;
- подготовить отчет по лабораторной работе.
Краткая характеристика объекта изучения
Статические структуры данных (обычные переменные, массивы, структуры, объединения и т.п.) имеют, раз навсегда, заданные для них объем памяти и соответственно диапазон возможных значений.
Множество задач требуют хранения данных с более сложной структурой, в процессы вычисления должны меняться не только значения переменных, но и их структура и соответственно размер выделяемой памяти. Такие данные называются данными с динамической структурой. Иногда их называют данными с рекурсивной структурой [4].
Пример генеалогическое дерево определяется как имя человека + 2 дерева его родителей, такое определение ведет к бесконечной структуре, реальные деревья всегда конечны.
Особенность рекурсивных структур – способность изменять размер. Для этого используется метод динамического распределения памяти, т.е. память под отдельные компоненты выделяется в тот момент, когда они начинают существовать. Это можно реализовать с помощью указателей.
Простейшая динамическая структура данных – линейный односвязный список. Каждый элемент списка содержит информационное поле, и указатель на следующий элемент списка, в последнем элементе этот указатель обычно равен 0. Для работы со списком достаточно хранить в оперативной памяти в отдельной переменной указатель на первый элемент списка. Схема линейного односвязного списка представлена на рисунке 7.
Информационное поле Указатель на след. элемент |
Информационное поле Указатель на след. элемент |
Информационное поле NULL |
Указатель на 1-ый элемент |
Рисунок 7 – Схема линейного односвязного списка
Структура, описывающая линейный односвязный список:
struct Inform { ... }; // Информационная структура (задает информационное поле)
struct List1
{
Inform Inf;
List1 *pNext;
};
Для работы с таким списком необходимо знать указатель на первый элемент списка (признак последнего элемента pNext==NULL), иногда удобно хранить указатель на последний элемент для ускорения доступа к последнему элементу, например, чтобы добавить элемент в конец списка. Более подробно с теоретическими основами для работы со списками можно ознакомиться в [3, 4].
Задачи и порядок выполнения работы
В лабораторной работе необходимо организовать список объектов и сортировку списка. Данные списка вводятся с клавиатуры, после каждого элемента идет запрос на ввод следующего элемента или завершение ввода (число элементов заранее неизвестно). При сортировке элементы списка остаются в оперативной памяти на «своих местах», меняются только значения указателей, связывающие элементы. Вывести на экран список до сортировки и после сортировки.
Пример выполнения работы
Условие задачи:
Объект списка- сотрудник (поля: ФИО, дата приема на работу, должность, базовый оклад). Сортировка по полю «оклад» методом прямого выбора.
Для решения задачи в среде Microsoft Visual Studio 2013 было создано стандартное консольное приложение (проект типа Win32 Console Application) с установленным свойством «пустой проект» (Empty project). В проект добавлен файл с расширением .cpp, исходный код которого приведен ниже.
Листинг программы с комментариями:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
struct Sotr // Сотрудник
{
char fio[64]; // ФИО
char date[16]; // Дата рождения
char dolg[32]; // Должность
double okl; // Оклад
};
struct List // Список
{
Sotr sotr; // Инф поле
List *pNext; // Указательна следующий элемент
};
// Функция добавления элемента в начало списка
void addFirst(List *& pF, // Указатель на начало списка
List* p) // Указатель на добавляемый элемент
{
p->pNext = pF;
pF = p;
}
// Удаление элемента из начала списка
List * delFirst(List *&pF) // Функция возвращает указатель на удаляемый элемент
{
if (pF == 0) return 0;
List *p = pF;
pF = pF->pNext;
return p;
}
// Добавление элемента перед заданным
bool add(List *&pF, List * pZad, List *p)
{
// Функция возвращает true при нормальном завершении и false в случае ошибки
if (pZad == pF) // Элемент будет первым
{
p->pNext = pF;
pF = p;
return true;
}
List *pPred = pF; // Указатель на предыдущий элемент перед pZad
while (pPred->pNext != pZad && pPred->pNext)
pPred = pPred->pNext;
if (pPred->pNext == 0) return false; // Элемента pZad нет в списке
p->pNext = pZad;
pPred->pNext = p;
return true;
}
// Удаление любого элемента p из списка
List * del(List*& pF, List *p) // Функция возвращает указатель на удаленный элемент
{
if (pF == 0) return 0;
if (pF == p) // Удаляем первый элемент
{
pF = pF->pNext;
return p;
}
else
{
List *pPred = pF; // Указатель на предыдущий элемент перед p
while (pPred->pNext != p && pPred->pNext)
pPred = pPred->pNext;
if (pPred->pNext == 0) return 0; // Элемента p нет в списке
pPred->pNext = p->pNext;
return p;
}
while (delFirst(pF)); // Очистка списка
}
int main(int argc, char* argv[])
{
List *pF = 0; // Список пуст
List *p;
// Ввод списка
char Ch; // Переменная для ввода условия продолжения ввода
do
{
p = (List *)malloc(sizeof(List)); // Выделяем память под элемент
printf("\nFIO: ");
fflush(stdin); gets_s(p->sotr.fio, 63);
printf("Date: ");
fflush(stdin); gets_s(p->sotr.date, 15);
printf("Dolg: ");
fflush(stdin); gets_s(p->sotr.dolg, 31);
printf("Okl=");
fflush(stdin); scanf_s("%lf", &p->sotr.okl);
addFirst(pF, p); // Добавляем элемент в начало списка
printf("For continue press Y or y else any key! ");
Ch = _getche(); // Чтение кода клавиши с печатью символа
} while (Ch == 'Y' || Ch == 'y');
// Вывод спика
for (List *pi = pF; pi; pi = pi->pNext) // Просмотр списка
printf("\n%s %s %s oklad=%.2f", pi->sotr.fio, pi->sotr.date,
pi->sotr.dolg, pi->sotr.okl);
// Сортировка списка
for (List *pi = pF; pi->pNext;)
{
// Ищем минимальный элемент в списке
double min = pi->sotr.okl;
List *pmin = pi;
for (List *pj = pi->pNext; pj; pj = pj->pNext)
if (pj->sotr.okl<min)
{
min = pj->sotr.okl;
pmin = pj;
}
if (pi != pmin) // Минимальный элемент делаем первым, он будет перед pi
{
del(pF, pmin);
add(pF, pi, pmin);
}
else pi = pi->pNext;
}
// Печать списка после сортировки
printf("\nSrting:");
for (List *pi = pF; pi; pi = pi->pNext) // Просмотр списка
printf("\n%s %s %s oklad=%.2f", pi->sotr.fio, pi->sotr.date,
pi->sotr.dolg, pi->sotr.okl);
printf("\nFor exit press any key ");
system("pause"); // Останавливаем программу, ждем нажатия любой клавиши
return 0;
}