{ /* Бесконечный цикл работы с базой данных*/ printf("1.Ввод сведений о новом сотруднике \п") ; printf("2.Удаление сведений о сотруднике \п"); printf("3.Печать содержимого базы данных \п"); printf("4.Сохранение базы данных в файле \п");
Глава 8. Примеры разработки программ
403
printf("5.Загрузка базы данных иэ файла \п"); printf("6.Инициализация базы данных \п"); printf("7.Окончание работы \п");
/* Ввод и обработка номера работы */ printf("\п\п Введите номер пункта меню\п"); scanf("%s", numb );
switch( numb[0] )
{
case '1':
input( );/* Ввод данных о новом сотруднике*/ break ;
case 2 ':
delete( );/*Удаление сведений о сотруднике*/ break ;
case '3':
print( );/* Печать содержимого базы данных*/ break ;
case '4' :
save( );/* Сохранение базы данных в файле */ break ;
case ’5':
load( ); /* Загрузка базы данных из файла */ break ;
case 'б ' :
init( ); /* Инициализация базы данных */ break ;
case '7' :
return ; /* Выход из программы */ default:
printf("Неверно указан номер пункта меню\п");
>
} /* Конец цикла */
}
Примечание. Имена всех переменных, встречающихся до void main(), являются внешними для остальных функций программы, и их не надо описывать в функциях еще раз, если главная функция, внешние переменные и остальные функции программы будут собраны в один файл (исходный модуль).
Функция init() - "Инициализировать базу данных". Пер вое действие, которое должен выполнить пользователь при ра боте с базой данных, - это подготовить ее к работе (инициали-
26*
404
Программирование на языке Си
зировать). Функция init() связывает все элементы массива структур в "список свободных элементов" и устанавливает на чальные значения управляющих переменных.
В тексте функции для доступа к элементам структуры ис пользована операция ("стрелка"). Напомним, что она изо бражается двумя символами: ("минус") и ">" ("больше, чем") и называется операцией косвенного выбора элемента структуры, адресуемой указателем. Операция используется с указателем на структуру для доступа к элементу структуры.
Выражение point -> prior обеспечивает доступ к элементу prior той структуры, которую адресует указатель point. Иными словами, point -> prior эквивалентно st[i],prior, если ранее вы полнен оператор point=&st[i]; Ниже приводится текст функции init():
int init(void)/* Инициализировать базу данных */
{
struct person
*point=st;
/* Указатель на
int
i ;
текущий
элемент */
/* Число занятых элементов */
ctrl.nb = 0 ;
ctrl.nf = 0 ;
/* Число свободных элементов */
Ctrl.bbeg=NULL;
/*
Списка
занятых
C t r l .bend=NULL;
элементов нет */
C t r l . f b e g = s t ;
/ * s t - а д р е с н а ч а л а
*/
Ctrl.fend = st;
массива
структур
point = st;
Инициализация массива. \n");
printf("\n\n
for
(i=0; i<M-l;
i++ )
{
( ctrl.nf!=0
)
/* He
первый элемент: */
if
t
point -> prior = point-1;
point -> next = point+1;
>
/* Первый элемент:
*/
else
{
point -> prior = NULL; point —> next = point+1;
Глава 8. Примеры разработки программ
405
>
point++;
C t r l .nf++;
Ct r l .fend++;
}/* Конец цикла инициализации (М-1) элементов массива */
/ * И н и ц и а л и з а ц и я п о с л е д н е г о э л е м е н т а в м а с с и в е * / c t r l . n f + + ;
point -> prior=point-l; point -> next= NULL; return 0;
}
В этой функции для каждого элемента массива в цикле зада ются значения указателей на предыдущий и следующий элемен ты. Для первого элемента указатель prior устанавливается рав ным NULL. Для последнего элемента указатель next устана вливается также равным NULL (для этих элементов нет соот ветственно предыдущего и следующего элементов).
Функция delete() - "Удалить все сведения о сотруднике из базы данных". В работе функции delete() можно выделить два этапа:
•поиск нужного элемента в списке занятых элементов;
•удаление найденного элемента (возврат элемента в список свободных элементов).
Действия по поиску нужного элемента в списке целесообраз но оформить в виде отдельной функции Find(), текст которой приводится ниже. Аргументом (ключом) поиска элемента явля ется фамилия сотрудника, Для сравнения строк (фамилия, вве денная с терминала, и фамилия в структуре) используется стан дартная библиотечная функция strcm p(), параметрами которой являются указатели на сравниваемые строки. Функция strcm p(), описанная в заголовочном файле string.h, возвращает 0, если строки совпадают. Для сравнения строк можно было бы использовать функцию row(), текст которой приведен в §5.3, где рассматриваются строки как параметры функций.
Функция поиска элемента в списке занятых Find() должна возвращать указатель на найденный элемент либо NULL, если
/* Перемещаем указатель на следующий элемент списка */
/* nb - число занятых элементов */
406
Программирование на языке Си
элемент в списке не обнаружен. Конструкция struct person * перед именем функции find() сообщает компилятору языка Си, что функция возвращает указатель именно на структуру типа person. Приведем текст функции Fmd():
#±nclude <string.h> /* Для функции strcmp’*/ struct person *find(char *nam)
{
int i;
struct person *ptf; /* Вспомогательный указа тель на начало списка занятых элементов */
ctrl.ptf = bbeg;
for (i=0; Kctrl.nb; i++)
{
if ((strcmp(nam, ptf->name)) == 0) return ptf;
ptf = ptf->next;
}
/* Просмотрен весь список Занятых элементов */ return NULL;
}
При выполнении функции delete() удаления элемента из списка занятых производятся следующие действия:
•запрос фамилии;
•поиск фамилии в списке занятых элементов;
•удаление найденного элемента из списка занятых;
•возврат освобожденного элемента в список свободных.
Текст функции delete() может быть таким:
/* Удаление элемента из списка занятых: */ int delete(void)
{
char nam[20]; /* фамилия */ int i ;
struct person *ptr; /* Указатель на удаляемый элемент */
printf("\n\n Функция удаления элемента \п"); if (ctrl.nb = = 0)
Глава 8. Примеры разработки программ
407
{
printf(" список занятых элементов пуст \п"); return -1;
}
/* Запрос фамилии */
printf(" Введите : Ф.И.О. \п"); scanf("%s", пат);
/* Поиск фамилии: */
if ((ptr=find(nam))==0)/* Фамилия не найдена*/
{
printf(" фамилия не найдена\п"); return -1;
>
Фамилия
„
элемент:
*/
/*
найдена, удалить
if
(ptr ==
Ctrl.bbeg)
*/
{
/* Это
первый элемент
i f
( C t r l . n b = = 1)
{/* Единственный элемент в списке занятых */
'Ctrl.bend = NULL; fr(ptr);
return 0;
)
else
( /* He единственный элемент */
C t r l . b b e g = p t r - > n e x t ;
Ctrl.bbeg->prior = NULL; fr (ptr) ;
return 0;
)
)
else
{/* Это не первый элемент */
if
( p t r
= = C t r l . bend)
{/*
Это
последний элемент (не единственный)*/
ptr->prior->next = NULL;
C t r l . b e n d = p t r - > p r i o r ; f r ( p t r ) ;
r e t u r n 0 ;
{ /* Элемент в середине */ ptr->prior->next = ptr—>next; ptr->next->prior = ptr->prior;
408
Программирование на языке Си
f г ( p t r )
;
r e t u r n
0 ;
}
>
>
В функции delete() использовано обращение к функции fr(), текст которой приведен ниже. Функция fr() "привязывает" ос вобожденный элемент к списку свободных элементов, делая его головным, и корректирует глобальные управляющие перемен ные и указатели. Встретившийся в функции delete() оператор
p t r - > p r i o r —> n e x t = p t r - > n e x t ;
имеет следующий смысл (рис. 8.6 ч- 8.9). Так как операция "стрелка" (->) выполняется слева направо, то оператор можно записать так:
( p t r - > p r i o r ) - > n e x t = p t r - > n e x t ;
Это означает, что в поле next элемента, стоящего слева от удаляемого, заносится адрес элемент», который находится спра ва от удаляемого (рис. 8.7 ч- 8.9). На рис. 8.7 изображено исход ное состояние массива структур, а на рис. 8.8 и рис. 8.9 - изме нение значений указателей после удаления элемента из списка занятых элементов и возврата его в список свободных эле ментов.
Аналогично следует понимать и выражение
p t r - > n e x t - > p r i o r = p t r - > p r i o r ;
□
I
ptr
Р и с . 8 . 6 . С в я з и э л е м е н т а , н а к о т о р ы й с с ы л а е т с я у к а з а т е л ь p tr (N - n e x t, Р - p r io r )
, 8. Примеры разработки программ 409
bbeg
bend
fbeg
fend
|NULL| |N {— ►|p |
LT
|N 1— »|p Г |NDLL|
INOUJ
IN -ft -T»|P| INUIJLI
Рис. 8.7. Исходное Состояние массива структур (N - next, Р - prior)
bbeg
bend
fend
i
INULLI
I N K - TH P I 1NULL)
t.J
Рис. 8.8. Изменение значений указателей после удаления элемента
Рис. 8.9. Изменение значений указателей после возврата элемента в список свободных
410
Программирование на языке Си
Функция fr() - "Возвратить освобожденный элемент в список свободных элементов". Эта вспомогательная функция используется в функции delete():
/* Освобождение удаляемого элемента: */ int fг (struct person *ptr)
{
if (ctrl.nb == M ) /* Были Заняты все элементы
p t r - > p r i o r
= NULL;
p t r - > n e x t
= NULL;•
Ctrl.fbeg->prior = ptr;
C t r l . f b e g = p t r ;
>
e l s e
{
ptr->prior = NULL; ptr—>next = ctrl.fbeg;
C t r l . f b e g - > p r i o r = p t r ; c t r l . f b e g = p t r ;
}
C t r l . n b — c t r l . n f + + ; r e t u r n 0 ;
}
Функция input() - "Ввести в базу данных сведения о но вом сотруднике". Функция input() должна вводить информа цию о новом сотруднике, сохраняя алфавитный порядок записей в базе данных. При реализации этой функции возникают труд ности: буквы национального алфавита (в данном случае кирил лицы) в международной таблице кодов не упорядочены по возрастанию внутренних кодов, что требует для операций упо рядочения дополнительных перекодировок с применением про межуточных словарей. Чтобы в рассматриваемой задаче не отвлекаться на решение этой проблемы, договоримся, что при вводе новой записи в уже существующую базу данных добав ляемый элемент подсоединяется к хвостовому элементу списка занятых элементов. Однако при этом необходимо помнить, что
Глава 8. Примеры разработки программ
411
алфавитный порядок в списке занятых элементов будет нару шен.
Как было решено ранее, информацию о новом сотруднике будем вводить в первый свободный элемент списка с после дующей привязкой этого элемента к списку занятых. Приведем текст функции input():
/* Вставка нового элемента в список занятых элементов: */
int input(void)
{
struct person *ptr; /* Вспомогательный указатель */
printf("\n\n Функция вставки элемента \п "); if (ctrl.nf = = 0)
{
printf(" Свободных элементов нет\п"); return -1;
>
ptr = fbeg;
/* Запрос Ф .И .О . */
printf(" Введите: Ф.И.О. \п"); scanf("%s", fbeg->name); printf("Введите код отдела \п");