412 |
|
Программирование на языке Си |
if (Ctrl.nf==l) /* Занимаем последнее место |
*/ |
{ |
Ctrl.fbeg = NULL; |
|
|
|
|
ptr->next = NULL; |
|
|
ptr—>prior = ctrl.bend; |
|
|
C t r l . b e n d - > n e x t = p t r ; |
|
|
. C t r l . b e n d = p t r ; |
|
} |
C t r l ; fend = |
NULL; |
|
|
|
|
e l s e |
|
|
{ |
if ( C t r l .nb |
== 0) |
|
|
|
|
C t r l . b b e g = p t r ; |
|
|
C t r l . f b e g = p t r - > n e x t ; |
|
|
p t r - > n e x t = |
NULL; |
|
|
p t r - > p r i o r = c t r l . b e n d ; |
|
|
C t r l . b e n d - > n e x t = p t r ; |
|
|
c t r l . b e n d = p t r ; |
|
|
C t r l .fbeg-> p r i o r = NULL; /* Новый первый в |
> |
списке свободных элементов |
*/ |
|
|
C |
t r l . пЪ++; |
|
|
C |
t r l . n f — ; |
|
|
r e t u r n 0 ;
Функция print() - "Печать списка занятых элементов".
Реализуем функцию печати списка занятых элементов в том по рядке, в котором они связаны. В качестве упражнений само стоятельно разберите варианты функции печати с ранжирова нием выводимой информации по окладу, кодам отделов, кодам должностей, дате поступления на работу.
Функция print() может быть реализована следующим обра зом:
/* Печать информации иа базы данных: */ int print(void)
{
int i ;
struct person *ptr; /* Вспомогательный указатель * / ,
Глава 8. Примеры разработки программ |
|
|
|
413 |
printf("База данных |
\"сотрудники\" |
\n\n\n"); |
if (ctrl.nb = 0 ) |
/* База данных |
пуста */ |
{ |
|
|
|
|
\п"); |
|
printf("База данных пуста |
|
return |
-1; |
|
|
|
|
|
|
} |
0; |
i < |
ctrl.nb; |
i++ |
) |
|
for ( i = |
|
{ |
|
фамилия: |
%s\n", |
i, |
ptr->name); |
printf("%d |
printf("отдел |
%s\n", ptr->depname); |
printf("код отдела |
%d\n", |
ptr->depnumb); |
printf("оклад |
%d\n", ptr->price); |
printf("код должности |
: %d\n", |
ptr->job); |
printf("должность : %s\n", ptr->jobname); printf("дата поступления; %s\n", ptr->date); ptr = ptr->next;
}
printf("\n\n Общее число Записей =%d\n", i); return 0;
}
Сохранение (восстановление) базы данных. Поскольку в рассматриваемом примере база данных реализована в виде дву направленного списка в основной памяти и она стирается (ее со держимое исчезает) при завершении работы программы, необ ходимо предусмотреть как сохранение базы данных во внешней памяти, так и загрузку базы данных из внешней памяти в сле дующем сеансе работы с ней.
Перед разработкой функции "выгрузка" базы данных в файл на диске необходимо определить формат и состав выгружаемых данных.
Собственно база данных (в виде двух независимых двуна правленных списков - списка занятых элементов и списка сво бодных элементов) располагается в массиве st структур типа struct person (см. главную программу). Информация о количе стве элементов в этих списках, а также указатели на начало и конец списков располагаются в структуре Ctrl типа struct control.
414 |
Программирование на языке Си |
|
Рассмотрим один из простейших вариантов создания на дис |
ке резервной копии базы данных о сотрудниках учреждения. Он состоит в том, что сначала необходимо записать в файл управ ляющую информацию (содержимое структуры Ctrl), а за ней - весь массив структур (байт за байтом), тем более что в рассмат риваемом примере он невелик (7 Кбайт). При восстановлении базы данных из файла на диске читается управляющая инфор мация и размещается в структуре Ctrl, затем читается информа ция, расположенная в файле за управляющей информацией, и размещается в области основной памяти, отведенной под массив структур st.
Такая схема копирования (восстановления) обладает сущест венным недостатком: вместе с содержательной информацией в файл записываются и значения указателей (адреса основной па мяти), содержащиеся в элементах списков базы данных. Таким образом, при принятой схеме копирования (восстановления) программа обслуживания базы данных будет работать при вы полнении условия неизменности адреса загрузки программы в основную память. При работе на персональном компьютере в операционных системах MS-DOS и UNIX (однопользова тельский режим) вызванная для выполнения программа будет загружена на то же самое место в основной памяти только в том случае, если между сеансами работы с программой не произво дились "системные" работы, повлекшие за собой перераспреде ление основной памяти.
Однако, несмотря на неполную надежность восстановления, приведем исходные тексты функций save() - "выгрузить базу данных в файл на диске" и Ioad() - "восстановить базу данных из файла". В этих функциях показано, как можно выгрузить (и затем восстановить) из основной памяти в файл на диске содер жимое переменных различных типов.
Операция sizeof, примененная в функциях save() и load(), вычисляет длину операнда в байтах. Операторы с = (char *)pti; и с = (char *)ptr; производят явное преобразование указателя pti на целое и указателя ptr на структуру соответственно в указате ли на символы, что позволяет использовать последние в Качест
Глава 8. Примеры разработки программ |
415 |
ве аргументов в функциях putc() H,getc() для побайтного обме на с файлом.
В функции save() производится запись в файл на диске всех элементов массива структур. Запись производится функцией putc() побайтно. Поэтому в программе необходимо описать указатель на символы, т.е. типа char*. Для работы с массивом структур необходим указатель на структуру типа person, содер жащую сведения о сотруднике учреждения, и указатель потока, связанный с файлом, в котором производится сохранение базы данных. Назовем этот файл bd.dat.
Приведем текст функции save():
/* Выгрузка в файл всего массива структур */ int save(void)
{
int i , k ;
struct person *ptr; /* Указатель на структуру типа person */
char *c; /* Указатель на символ */ int *pti; /* Указатель на целое */ FILE *fp; /* Указатель на поток */
/* |
Открыть |
файл для записи: */ |
if |
((fp = |
fopen ("bd. dat" ,"w"))— NULL) |
{
perror("bd.dat"); return -1;
} •
if (ctrl.nb == 0) /* База данных пуста */
{
puts("База данных пуста \п");
puts("Запись на диск не производится \п"); return -1,'
}
else
{
puts(" |
Производится |
запись БД в файл "); |
ptr |
= |
st; |
/* Начало массива структур */ |
/* Запись |
управляющей информации: */ |
pti |
= |
&ctrl.nb; |
/* |
Начало блока управляющей |
с = |
(char |
*)pti; |
/* |
информации |
*/ |
Настройка |
указателя */ |
416 |
|
|
|
Программирование на языке Си |
/* |
Цикл ввода |
управляющей информации: */ |
for |
(i=0; |
Ksizeof (ctrl) ; |
i++) |
putc(*c++, |
fp); |
|
/* Цикл |
вывода в |
файл всего массива структур: */ |
с = |
(char |
*)ptr; |
/* Настройка указателя */ |
for(к = 0 ; |
к < |
sizeof(st); |
к++) |
|
putc(*c++, |
fp); |
|
<fclose(fp); |
|
|
|
return 0; |
|
|
|
|
Функция load() может быть реализована следующим обра зом:
/* Ввод (восстановление) из файла всего массива структур */
int load(void)
(
int i ;
struct person *ptr; /‘Указатель на структуру*/
int |
|
*pti; |
d; |
char |
*c, |
FILE |
*fp; |
/* Указатель на поток */ |
/* |
Открыть файл для чтения: */ |
if |
((fp = |
fopen("bd.dat","r")) = NULL ) |
.(
perror ("bd.'dat") ; return -1;
}
if( ctrl.nb = = 0 )
puts(" База данных пуста \n"); if(Ctrl.nb != 0) /* База данных не пуста */
(
puts("База в основной памяти не пуста\п"); puts("Производить загрузку? [у,п]:");
d = getchar( );
if (d = = 'N' || d = = 'n' )
{•
puts("Запрос на загрузку базы отменен\п"); return 1;
Глава 8. Примеры разработки программ |
417 |
puts("Производится загрузка |
базы данных\п"); |
/* |
Чтение |
управляющей информации: */ |
pti |
= &ctrl.nb; |
|
с = |
(char |
*)]bti; |
|
/* Цикл ввода в список из файла */ |
for(i=0; |
Ksizeof (ctrl) ; |
i++) |
ptr |
*c++=getc(fp); |
|
= st; |
/* Начало массива структур */ |
с = |
(char |
*)ptr; |
|
/* Цикл ввода всего массива */ |
for(i = 0 ; |
i < sizeof(st); |
i++) |
(
if ((*c=getc(fp)) != EOF)
C + + ;
else
{
fclose(fp); return 0;
}
}
return 0;
}
Перед восстановлением базы данных с диска проверяют, пуст ли список занятых элементов в основной памяти. Если нет, то выдается запрос "Производить загрузку?". В случае неполо жительного ответа, обозначаемого латинской буквой N или п, загрузка информации в БД из файла не производится.
Для того чтобы программа обслуживания базы данных стала не зависимой от адреса загрузки в основную память, модерни зируем функции save() и load( ) следующим образом:
•будем выгружать (записывать) в файл не весь массив структур, а только список занятых элементов;
•после чтения содержимого файла в массив структур скор ректируем значения указателей, образующих связи в связ ных списках базы данных, т.е. установим реальные адреса в указателях.
На рис. 8.10 показаны состояние списка занятых элементов в массиве структур до выгрузки в файл, структура файла с ин формацией из базы и состояние массива структур после восста-
418 |
Программирование на языке Си |
новления базы данных из файла. В общем случае элементы спи ска, содержащего сведения о сотрудниках, к концу сеанса рабо ты с базой данных могут располагаться не в соседних элементах массива. Однако после выгрузки в файл эти элементы окажутся рядом. Читая из файла в массив структур список занятых эле ментов байт за байтом, мы размещаем элементы списка с начала массива структур, т.е. элементы списка, в котором хранятся све дения о сотрудниках, в общем случае попадают на новые места в массиве структур, и требуется корректировка адресов в эле ментах этого списка. Эта процедура аналогична процедуре ини циализации массива структур перед началом работы с базой данных. Началом списка свободных элементов будет первый элемент массива структур, не занятый загруженной из файла информацией.
Исходное
состояние
массива
структур
|
Сохранение |
Ч |
|
|
|
|
(запись) |
|
|
|
|
Файл |
Управляющая |
|
N U L L 1 N |
Р 2 N |
Р 3 N U L L |
|
информация |
|
|
|
• |
|
|
|
|
|
|
|
|
Восстановление / |
|
|
|
|
(чтение) |
|
|
|
|
Массив |
|
|
|
|
|
структур |
N U L L 1 |
N |
Р 2 N |
Р |
3 N U L L |
после |
|
|
|
|
|
восстановления базы данных из файла
Рис. 8.10. Второй вариант схемы сохранения (восстановления) базы данных
Глава 8. Примеры разработки программ |
419 |
Приведем текст функции save2() - "сохранить в файле на диске только список занятых элементов":
/* Запись в файл только Занятых элементов */ int save2(void)
{
int size;/* Размер элемента списка в байтах */ int i , k ;
struct person *ptr; /* Указатель на структуру типа person */
char *c; /* Указатель на символ */ int *pti; /* Указатель на целое */ FILE *fp; /* Указатель на поток */ /* Открыть файл для записи: */
if ((fp - fopen("bd.dat","w"))== NULL)
{
perror("bd.dat"); return -1;
}
if (ctrl.nb == 0) /* База данных пуста */
{
puts("База данных пуста \п");
puts("Запись на диск не производится \п") ; return -1;
}
else
{
puts("\n Производится запись БД в файл "); size = sizeof(st)/М;
/* size - длина одного элемента в байтах */ /* М - число элементов в массиве структур*/
ptr |
= st; |
/* Начало массива структур */ |
/* Запись |
управляющей информации: |
*/ |
pti |
= fictrl.nb; |
/* Начало |
блока |
управляющей |
с = |
(char |
*)pti; |
информации */ |
|
/* Цикл ввода управляющей информации: */ |
for |
(i=0; |
i<sizeof(Ctrl); |
i++) |
|
|
putc(*c++, fp); |
списка занятых |
/* Цикл вывода в |
файл |
for |
элементов */ |
|
k++) |
|
|
(k=0; |
k<ctrl.nb; |
|
|
420 |
Программирование на языке Си |
с =. (char |
*)ptr; |
/* Цикл вывода одного элемента списка */ |
for (i=0; |
i<size; i++) |
putc(*c++, fp);
/‘Установка указателя на следующий элемент*/ ptr = ptr->next;
}
fclose(fp); return 0;
}
}
Приведем текст функции load2() - "загрузить в основную память (прочитать из файла) базу данных из файла с корректи ровкой адресов, содержащихся в указателях элементов списков базы данных".
/* |
Загрузка (чтение) |
базы данных из |
файла |
int |
с корректировкой |
указателей |
*/ |
load2(void) |
|
|
{
int i, k;
struct person *ptr;/* Указатель на структуру*/
int |
|
*pti; |
|
char |
*c, d; |
/* Указатель на файл (поток) */ |
FILE |
*fp; |
/* |
Открыть |
файл для чтения: */ |
if |
((fp=fopen("bd.dat","г"))== NULL ) |
{
puts("Ошибка при открытии файла \п") ; return 1;
>
if (cnrl.nb == 0)
puts(" База данных пуста \п");
if (nb *= 0) /* База данных не пуста */
{
puts("База в основной памяти не пуста\п"); puts("Производить загрузку? [у,п]:");
d = |
getchar( ); |
if |
(d=='N' || d=='n ' ) |
{ |
|
puts("Запрос на загрузку базы отменен\п"); return 1;
Глава 8. Примеры разработки программ |
421 |
}
}
puts("Производится загрузка |
базы данных\п"); |
/* Чтение управляющей информации */ |
|
pti = |
Sctrl.nb; |
|
|
|
|
|
|
с = |
(char |
*)pti; |
|
|
|
i++) |
|
for |
(i=0; |
Ksizeof (Ctrl) ; |
|
|
*c++=getc(fp); |
|
|
|
|
|
/* Цикл чтения списка из файла */ |
|
ptr = |
st; |
/* Начало массива структур */ |
for |
(k=0; |
k<sizeof(st)/M*ctrl.nb; |
k++) |
{ |
|
(char *)ptr; |
|
|
|
|
|
|
c = |
|
|
|
|
|
|
/* Цикл чтения одного элемента */ |
|
for- (i=0; |
Ksizeof (st) /М; |
|
i++) |
|
( |
if ((*c=getc(fp)) |
!= EOF) |
|
|
|
|
|
|
|
C + + ; |
|
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
|
|
( |
fclose(fp); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
return |
0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
/* Загружаем в последовательные |
ptr++; |
} |
/* |
|
|
элементы массива |
*/ |
|
Конец цикла |
по |
к |
*/ |
|
на последний |
C t r l . bend = p |
t r ; |
/* |
Указатель |
ptr++; |
/* |
Первый |
|
занятый |
элемент |
*/ |
свободный |
элемент |
*/ |
C t r l .fbeg |
= p |
t r ; |
|
|
|
|
|
|
p t r - > p r i o r |
= NULL; |
|
|
|
|
|
|
/* Корректировка указателей в списке занятых
элементов */ |
|
|
ptr |
= st; |
Kctrl.nb; |
i++) |
for |
(i=0; |
{ |
Первый |
элемент ? |
*/ |
/* |
if |
(ctrl.nf = |
0) |
|
{ |
|
|
|
|
ptr->prior = NULL; |
|
ptr->next |
= ptr+1; |
|