Чтобы завершить обсуждение двусвязных списков, в данном разделе представлена простая, но законченная программа для работы со списком рассылки. Во время работы весь список хранится в оперативной памяти. Тем не менее, его можно сохранять в файле и загружать для дальнейшей работы.
/* Простая программа для обработки списка рассылки
иллюстрирующая работу с двусвязными списками. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct address {
char name[30];
char street[40];
char city[20];
char state[3];
char zip[11];
struct address *next; /* указатель на следующую запись */
struct address *prior; /* указатель на предыдущую запись */
};
struct address *start; /* указатель на первую запись списка */
struct address *last; /* указатель на последнюю запись */
struct address *find(char *);
void enter(void), search(void), save(void);
void load(void), list(void);
void mldelete(struct address **, struct address **);
void dls_store(struct address *i, struct address **start,
struct address **last);
void inputs(char *, char *, int), display(struct address *);
int menu_select(void);
int main(void)
{
start = last = NULL; /* инициализация указателей на начало и конец */
for(;;) {
switch(menu_select()) {
case 1: enter(); /* ввод адреса */
break;
case 2: mldelete(&start, &last); /* удаление адреса */
break;
case 3: list(); /* отображение списка */
break;
case 4: search(); /* поиск адреса */
break;
case 5: save(); /* запись списка в файл */
break;
case 6: load(); /* считывание с диска */
break;
case 7: exit(0);
}
}
return 0;
}
/* Выбор действия пользователя. */
int menu_select(void)
{
char s[80];
int c;
printf("1. Ввод имени\n");
printf("2. Удаление имени\n");
printf("3. Отображение содержимого списка\n");
printf("4. Поиск\n");
printf("5. Сохранить в файл\n");
printf("6. Загрузить из файла\n");
printf("7. Выход\n");
do {
printf("\nВаш выбор: ");
gets(s);
c = atoi(s);
} while(c<0 || c>7);
return c;
}
/* Ввод имени и адресов. */
void enter(void)
{
struct address *info;
for(;;) {
info = (struct address *)malloc(sizeof(struct address));
if(!info) {
printf("\nНет свободной памяти");
return;
}
inputs("Введите имя: ", info->name, 30);
if(!info->name[0]) break; /* завершить ввод */
inputs("Введите улицу: ", info->street, 40);
inputs("Введите город: ", info->city, 20);
inputs("Введите штат: ", info->state, 3);
inputs("Введите почтовый индекс: ", info->zip, 10);
dls_store(info, &start, &last);
} /* цикл ввода */
}
/* Следующая функция вводит с клавиатуры строку
длинной не больше count и предотвращает переполнение
строки. Кроме того, она выводит на экран подсказку. */
void inputs(char *prompt, char *s, int count)
{
char p[255];
do {
printf(prompt);
fgets(p, 254, stdin);
if(strlen(p) > count) printf("\nСлишком длинная строка\n");
} while(strlen(p) > count);
p[strlen(p)-1] = 0; /* удалить символ перевода строки */
strcpy(s, p);
}
/* Создание упорядоченного двусвязного списка. */
void dls_store(
struct address *i, /* новый элемент */
struct address **start, /* первый элемент списка */
struct address **last /* последний элемент списка */
)
{
struct address *old, *p;
if(*last==NULL) { /* первый элемент списка */
i->next = NULL;
i->prior = NULL;
*last = i;
*start = i;
return;
}
p = *start; /* начать с начала списка */
old = NULL;
while(p) {
if(strcmp(p->name, i->name)<0){
old = p;
p = p->next;
}
else {
if(p->prior) {
p->prior->next = i;
i->next = p;
i->prior = p->prior;
p->prior = i;
return;
}
i->next = p; /* новый первый элемент */
i->prior = NULL;
p->prior = i;
*start = i;
return;
}
}
old->next = i; /* вставка в конец */
i->next = NULL;
i->prior = old;
*last = i;
}
/* Удаление элемента из списка. */
void mldelete(struct address **start, struct address **last)
{
struct address *info;
char s[80];
inputs("Введите имя: ", s, 30);
info = find(s);
if(info) {
if(*start==info) {
*start=info->next;
if(*start) (*start)->prior = NULL;
else *last = NULL;
}
else {
info->prior->next = info->next;
if(info!=*last)
info->next->prior = info->prior;
else
*last = info->prior;
}
free(info); /* освободить память */
}
}
/* Поиск адреса. */
struct address *find( char *name)
{
struct address *info;
info = start;
while(info) {
if(!strcmp(name, info->name)) return info;
info = info->next; /* перейти к следующему адресу */
}
printf("Имя не найдено.\n");
return NULL; /* нет подходящего элемента */
}
/* Отобразить на экране весь список. */
void list(void)
{
struct address *info;
info = start;
while(info) {
display(info);
info = info->next; /* перейти к следующему адресу */
}
printf("\n\n");
}
/* Данная функция выполняет собственно вывод на экран
всех полей записи, содержащей адрес. */
void display(struct address *info)
{
printf("%s\n", info->name);
printf("%s\n", info->street);
printf("%s\n", info->city);
printf("%s\n", info->state);
printf("%s\n", info->zip);
printf("\n\n");
}
/* Поиск имени в списке. */
void search(void)
{
char name[40];
struct address *info;
printf("Введите имя: ");
gets(name);
info = find(name);
if(!info) printf("Не найдено\n");
else display(info);
}
/* Сохранить список в дисковом файле. */
void save(void)
{
struct address *info;
FILE *fp;
fp = fopen("mlist", "wb");
if(!fp) {
printf("Невозможно открыть файл.\n");
exit(1);
}
printf("\nСохранение в файл\n");
info = start;
while(info) {
fwrite(info, sizeof(struct address), 1, fp);
info = info->next; /* перейти к следующему адресу */
}
fclose(fp);
}
/* Загрузка адресов из файла. */
void load()
{
struct address *info;
FILE *fp;
fp = fopen("mlist", "rb");
if(!fp) {
printf("Невозможно открыть файл.\n");
exit(1);
}
/* освободить память, если в памяти уже есть список */
while(start) {
info = start->next;
free(info);
start = info;
}
/* сбросить указатели на начало и конец */
start = last = NULL;
printf("\nЗагрузка из файла\n");
while(!feof(fp)) {
info = (struct address *) malloc(sizeof(struct address));
if(!info) {
printf("Нет свободной памяти");
return;
}
if(1 != fread(info, sizeof(struct address), 1, fp)) break;
dls_store(info, &start, &last);
}
fclose(fp);
}