Лаб. практикум
.pdf81
несортированными. Внутренние таблицы (создаваемые в оперативной памяти) могут храниться в виде вектора или в виде списка.
Пример решения задачи
Задача. Входной файл st.txt содержит сведения о сдаче студентами группы экзаменационной сессии. Каждая запись файла содержит фамилию и инициалы студента (15 символов) и пять оценок (5 символов) и завершается символом ” перевод строки”. Количество записей не более 30. Вывести:
-фамилии всех студентов с указанием среднего балла каждого студента;
-информацию о заданном студенте.
Метод решения задачи. Чтобы входной файл не читать многократно, в программе создается таблица, содержащая фамилии, оценки и средние баллы всех студентов. При поиске в таблице информации о заданном студенте ключом является фамилия студента. Таблица хранится в векторе, поскольку число студентов в группе ограничено (к примеру, не более 30).
Чтобы можно было получить информацию о нескольких студентах или обо всех по выбору пользователя, в программе в цикле выдается меню:
-------------------------------------------------------
Выберите номер пункта меню: 1 - средние баллы студентов
2 - информация о заданном студенте
3 - выход
-------------------------------------------------------
При выборе пункта 3 выполнение программы завершается.
/* Программа 9.1 на языке С++ */
#include <stdio.h> #include <conio.h> #include <string.h> #define MAX 30
// тип записи файла struct STUDENT
|
|
|
82 |
|
{ |
char |
fio[15]; |
// фамилии и.о. |
|
}; |
char |
oc[7]; |
// 5 оценок + '\n' + '\0' |
|
// тип элемента таблицы |
||||
|
||||
struct EL_TAB |
|
|||
{ |
char |
fio[15]; |
// фамилия и.о. |
|
|
int oc[5]; |
// оценки |
||
}; |
float |
srball; |
// средний балл |
|
|
|
|
// прототипы функций void PechTab (EL_TAB * tab, int n); void Stud (EL_TAB * tab, int n);
/* |
----------------------------*/ |
/* |
главная функция */ |
/*---------------------------- |
*/ |
void main() |
|
|
|
|
{ FILE *f; |
// указатель на входной файл |
|||
STUDENT tz; |
// текущая запись файла |
|||
EL_TAB tab[MAX]; |
// таблица |
|||
int |
n; |
// длина таблицы |
||
int |
i; |
|
|
|
float |
s; |
// сумма оценок |
|
if ((f= fopen("st.txt","r")) == NULL) { puts ("Файл st.txt не найден");
return;
}
// создание таблицы n = 0;
while (fgets((char *)&tz, sizeof(struct STUDENT), f) != NULL)
{for (i=0,s=0; i<5; i++)
{int ocenka = tz.oc[i]-'0'; tab[n].oc[i] = ocenka;
s += ocenka;
}
tz.fio[14]='\0';
strcpy(tab[n].fio,tz.fio); tab[n++].srball = s/5;
}
fclose(f);
char nom; // номер пункта меню do
{clrscr();
puts ("------------------------------------------------------");
|
83 |
puts (" |
Выберите номер пункта меню:"); |
puts ("1 |
- средние баллы студентов"); |
puts ("2 |
- информация о заданном студенте"); |
puts ("3 |
- выход"); |
puts ("------------------------------------------------------- |
"); |
nom = getche(); switch (nom)
{case '1': PechTab(tab,n); break; case '2': Stud(tab,n); break; case '3': break;
default: puts ("\nНужно ввести номер от 1 до 3");
}
if (nom != '3')
{puts("\nДля продолжения нажмите любую клавишу"); getch();
}
}
while (nom != '3');
}
/* |
-----------------------------------------------------------------*/ |
/* |
Печать среднего балла каждого студента */ |
/*----------------------------------------------------------------- |
*/ |
void PechTab (EL_TAB * tab, int n)
{int j;
puts ("\nФамилия и.о. Ср.балл");
puts ("------------------------------ |
"); |
for (j=0; j<n; j++) |
|
{ |
|
printf("%s %.1f\n", tab[j].fio, tab[j].srball);
}
}
// заглушка для функции вывода информации о студенте void Stud (EL_TAB * tab, int n)
{
}
Контрольные вопросы и упражнения
1.Что такое таблица?
2.Какая таблица называется линейной или последовательной?
3.Какие операции над таблицами выполняются в программе 9.1?
84
4. Как описать таблицу, содержащую название месяца и количество дней в каждом из 12 месяцев года?
5.Опишите таблицу из n элементов ( n ≤ 50). Каждый элемент состоит из 4-х полей, описывающих анкетные данные спортсмена:
-фамилия, имя и отчество;
-год рождения;
-рост в метрах (например, 1.85);
-вес в кг.
6.Опишите таблицу из n элементов ( n <=100), каждый элемент которой содержит следующие поля:
-№ поезда (целое число);
-пункт назначения (строка из 30 символов);
-время отправления (строка из 6 символов).
Задания
5.Ознакомьтесь с теоретическим материалом и примером решения задачи. Ответьте на контрольные вопросы.
6.Введите и выполните программу 9.1. Создайте файл st.txt для проверки программы и снова запустите программу.
7.Вместо заглушки функции Stud напишите функцию, которая запрашивает фамилию студента, выполняет линейный поиск элемента с заданной фамилией в таблице и выводит значение найденного элемента или сообщение об ошибке, если такой фамилии нет в таблице.
8.Добавьте в программу подпрограмму сортировки таблицы в порядке убывания среднего балла.
9.Добавьте подпрограмму вывода:
а) отличников группы; б) студентов со средним баллом не ниже 4;
в) студентов, имеющих двойки.
85
Л а б о р а т о р н а я р а б о т а N 10
Организация списков с помощью указателей и структур
Список - это последовательность элементов, в |
которой |
каждый |
||
элемент |
содержит ссылку на |
следующий элемент. |
Последний |
элемент |
содержит |
нулевую (пустую) |
ссылку. Доступ к списку обеспечивается с |
||
помощью |
указателя списка, |
ссылающегося на первый элемент списка. |
Таким образом, каждый элемент списка состоит из двух полей - поля информации и поля ссылки (рис. 10.1).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/ |
|
|
/ |
|
\ |
... |
/ |
|
||||||||
Указатель |
|
поле |
поле |
|
|
|
|
пустая |
||||||||
списка |
информации |
ссылки |
|
|
|
ссылка |
Рис. 10.1. Cписок
На языке С список организуется с помощью указателей и структур или же с помощью параллельных массивов.
Рассмотрим организацию списков с помощью указателей и структур. Допустим, программа создает список идентификаторов. Тогда в программе следует описать тип элемента списка в виде структуры из двух полей и указатель списка. Указатель списка и поле ссылки на следующий элемент описываются как переменные типа указатель на структуру. Например:
/* описание списка идентификаторов */
struct |
EL_SP |
// тип элемента списка |
|
{ char |
id [9]; |
|
// идентификатор |
struct EL_SP *sled; |
// ссылка на следующий элемент |
||
}; |
|
|
|
struct |
EL_SP |
*p; |
// указатель списка |
Обратите внимание: EL_SP - это не переменная, а имя типа структуры, память для элемента списка здесь не выделяется.
Чтобы выделить память для очередного элемента списка, нужно
86
вызвать функцию malloc(). Аргументом этой функции является размер памяти в байтах, а возвращает она адрес выделенного участка памяти (при использовании этой функции нужно включать файл stdlib.h). Например, оператор присваивания
p = (struct EL_SP *)malloc (sizeof(struct EL_SP));
выделяет память для первого элемента списка и устанавливает указатель p. Операция sizeof (srtuct EL_SP) определяет размер памяти в байтах для структуры типа EL_SP. Функция malloc() выделяет требуемый объем памяти и возвращает адрес этого блока памяти, который присваивается
указателю p. |
|
|
|
|
|
|
Освобождение памяти |
при |
удалении |
элемента |
из списка |
||
осуществляется функцией |
free(), где аргументом должен быть указатель на |
|||||
элемент списка, например, |
free (p). |
|
|
|
||
При программировании на языке С++ лучше использовать |
||||||
специальные операторы распределения памяти new |
и delete. Оператор new |
|||||
выделяет память, а delete – |
освобождает. Например: |
|
|
|||
EL_SP * i; |
// указатель на новый элемент списка |
|
||||
i = new EL_SP; // выделение памяти для нового элемента |
|
|||||
… |
|
|
|
|
|
|
delete i; |
// освобождение памяти, занимаемой элементом списка |
|||||
Доступ к отдельным |
полям |
структуры, |
на которую |
ссылается |
указатель, осуществляется с помощью операции ->. Например, для приведенного выше списка идентификаторов
p -> id - |
это поле |
id (идентификатор) |
1-го элемента списка, |
p ->sled - |
это поле |
sled 1-го элемента |
или адрес 2-го эл-та списка. |
Пустая ссылка обозначается константой NULL. Например, оператор p = NULL;
означает, что указатель списка ни на что не будет ссылаться, т.е. список становится пустым. Поле ссылки последнего элемента списка должно иметь значение NULL.
87
Пример решения задачи
Задача. Дана последовательность из n идентификаторов. Длина каждого идентификатора не более 8 символов. Напечатать идентификаторы в лексикографическом порядке.
Пример теста:
n=7
Исх.посл-ть: |
Результат: |
SIMV |
A |
X |
A1 |
A1 |
SIMV |
SL |
SL |
Z20 |
TEXT |
A |
X |
TEXT |
Z20 |
Метод решения. Задачу можно решить, сцепляя идентификаторы
всписок в лексикографическом порядке по мере их ввода. В
приведенной |
ниже |
программе |
после |
чтения |
очередного |
идентификатора |
сразу |
происходит |
добавление его |
в список, |
сформированный из предшествующих идентификаторов. На рис. 10.2 показано, как изменяется список в процессе включения в него очередных идентификаторов.
Еще несколько пояснений к программе. Идентификаторы поочередно вводятся в массив t_id с помощью функции gets(). Сравнение двух идентификаторов производится с помощью функции strcmp(), которая возвращает значение 0, если идентификаторы равны, и разность кодов двух соответствующих несовпавших символов, если идентификаторы не равны. Значит, значение функции будет < 0, если первый идентификатор в лексикографическом порядке должен следовать раньше второго, и значение будет > 0 в противном случае.
|
|
|
|
|
|
|
|
|
88 |
|
|
|
|
||
P |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SIMV |
0 |
|
|
|
|
|
|
|
|
|
||
P |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SIMV |
|
|
X |
0 |
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
P |
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A1 |
|
|
SIMV |
|
|
X |
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
P |
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A1 |
|
|
|
SIMV |
|
|
SL |
|
|
X |
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
. . .
P
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A |
|
|
A1 |
|
|
SIMV |
|
|
SL |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
X |
|
|
Z20 |
0 |
TEXT |
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Рис. 10.2. Пример процесса формирования списка идентификаторов
Функция strcpy() служит для копирования строк символов, имеет два аргумента: указатель на строку, куда копировать, и указатель на строку, откуда копировать.
/* Программа 10.1. (язык С) */ |
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <conio.h> |
|
|
#define MAXDL 9 |
/* макс.длина ид-ра (строки символов с |
|
|
признаком конца '\0' ) |
*/ |
struct EL_SP |
/* тип элемента списка */ |
|
{ char id [MAXDL]; |
/* идентификатор */ |
|
struct EL_SP *sled; |
/* ссылка на следующий элемент */ |
|
}; |
|
|
|
89 |
/*---------------------------------------------------------------------------------------- |
*/ |
/* |
функция включения очередного идентификатора в список */ |
/*---------------------------------------------------------------------------------------- |
*/ |
void Vkl ( struct EL_SP **p, char t_id[] ) |
|
|
/* Вх. данные: *p |
- указатель списка идентификаторов в |
|
|
лексикографическом порядке, |
|
t_id - включаемый в список (текущий) ид-р */ |
||
/* Вых. данные: *p |
*/ |
|
{ struct EL_SP *pt, |
/* указатель включаемого эл-та */ |
|
*k,*j; |
/* указатели очередного и предыдущего |
|
|
элементов списка |
*/ |
/* выделение памяти для нового эл-та списка */ pt = (struct EL_SP *) malloc(sizeof(struct EL_SP)); strcpy(pt->id, t_id);
if (*p==NULL || strcmp(pt->id,(*p)->id) < 0) { /* включение ид-ра в начало списка */
pt->sled=*p; *p=pt;
}
else
{ /* поиск элемента списка, после которого нужно включить идентификатор */
k=*p;
while (k!=NULL && strcmp(pt->id,k->id)>=0) { j=k; k=k->sled;
}
/* включение эл-та *pt после элемента *j */ j->sled=pt; pt->sled=k;
}
} |
|
|
/*----------------------------------------------------------------- |
|
*/ |
/* |
функция печати списка |
*/ |
/*----------------------------------------------------------------- |
|
*/ |
void PechSp ( struct EL_SP *p ) |
|
|
/* |
Вх. данные: p - указатель начала списка |
*/ |
{struct EL_SP *i; /* указатель текущего элемента списка */ printf ("\nРезультат:\n");
for ( i=p; i!=NULL; i=i->sled ) puts (i->id);
}
|
|
90 |
|
|
/*-------------------------------------------------------------- |
|
|
*/ |
|
/* О С Н О В Н А Я П Р О Г Р А М М А */ |
|
|||
/*------------------------------------------------------------- |
|
|
*/ |
|
main() |
|
|
|
|
{ struct EL_SP *p; |
/* указатель начала списка |
*/ |
||
unsigned |
n ; |
/* количество идентификаторов */ |
||
unsigned |
i ; |
/* параметр цикла |
*/ |
|
char t_id[MAXDL]; |
/* текущий идентификатор |
*/ |
||
printf ("\nВведите число идентификаторов\n n="); |
||||
scanf ("%u",&n); |
|
|
|
|
getchar(); |
/* пропуск символа "перевод строки" */ |
|||
p=NULL; |
/* список пока пуст */ |
|
|
|
printf ("Введите идентификаторы "); |
|
|
printf ("(после каждого нажимайте клавишу <Enter> )\n"); for ( i=1; i<=n; i++ )
{ gets (t_id);
Vkl (&p,t_id); |
/* включение ид-ра в список */ |
} |
|
PechSp (p); |
/* печать списка */ |
printf ("\n\nДля завершения нажмите любую клавишу\n"); getch();
}
Контрольные вопросы и упражнения
1.Приведите пример исходных данных для программы 10.1 и нарисуйте список, который сформирует программа.
2.Где в программе происходит формирование списка?
3.Почему функции Vkl() передается адрес указателя списка p, а
функции PechSp() – значение указателя p?
4. В функции Vkl() параметр p какого типа?
5.Как в функции Vkl() происходит обращение к указателю списка?
6.Изменится ли указатель списка p в главной программе, если функцию печати списка изменить следующим образом:
void PechSp ( struct EL_SP *p )
{
printf ("\nРезультат:\n");