Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

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; /* Вспомогательный указатель * / ,

ptr = Ctrl.bbeg;

Глава 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 показаны состояние списка занятых элементов в массиве структур до выгрузки в файл, структура файла с ин­ формацией из базы и состояние массива структур после восста-

27~3124

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;

 

}

Соседние файлы в папке книги