Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Программирование на языке Си. Учебное пособие для СПО
.pdf
for (i=0; i<N; i++) {
n[i]=i; f[i]=i;
}
for (i=0; i<N-1; i++)
for(j=0; j<N-1; j++) {
if( strcmp( a[f[j]].fam, a[f[j+1]].fam) > 0 ) {
t=f[j]; f[j]=f[j+1]; f[j+1]=t;
}
if( strcmp(name[a[n[j]].n],name[a[n[j+1]].n])>0) {
//или if(a[n[j]].n>a[n[j+1]].n) {
t=n[j]; n[j]=n[j+1]; n[j+1]=t;
}
}
for (i=0; i<N; i++)
printf("%-15s %-15s %-13s \n", name[a[i].n], a[i].fam, a[i].tel);
puts("");
for (i=0; i<N; i++)
printf("%-15s %-15s %-13s \n", name[a[n[i]].n], a[n[i]].fam, a[n[i]].tel);
puts("");
for (i=0; i<N; i++)
printf("%-15s %-15s %-13s \n", a[f[i]].fam, name[a[f[i]].n], a[f[i]].tel);
}
Контрольные вопросы
Какие типы данных относятся к простым, а какие – к составным?
Как объявляются структуры, чем отличается структура от массива?
Как обращаться к полям структуры?
Какие операции допустимы над структурой в целом?
71

10. Списки
Data1
Data2
Data3
null
Список – динамическая структура данных, строится на основе структур.
Обычно применяют, когда количество записей изменяется в процессе работы
программы. Используют структуры, состоящие из полей 2-х типов –
информационных (поля с данными) и ссылок.
Ссылки – содержат адреса других структур этого же типа. При
добавлении нового узла в список выделяется новый блок памяти, и
устанавливаются связи этого элемента с уже существующими. Для обозначения
конечного элемента в цепи используются нулевые ссылки (NULL).
В простейшем случае каждый элемент списка содержит всего одну
ссылку – односвязный список.
struct spis {
char data[80];
spis *next;
};
spis *head, *p, *tail;
Указатели head, tail используются для хранения адреса первого и
последнего элемента списка.
Каждый элемент содержит также ссылку на следующий за ним элемент.
У последнего в списке элемента поле ссылки содержит NULL. Чтобы не
потерять список, мы должны обязательно хранить адрес его первого узла – он
называется «головой» списка.
Память под элементы списка выделяется и освобождается динамически,
по мере необходимости, в отличие от массивов. Достоинством списков является
легкость добавления и удаления элементы из любого места. Недостаток –
список, в отличие от массива – последовательная структура данных, и ссылки
на элементы занимают дополнительное места в памяти.
Пример создания списка из 3 элементов
struct spis {
char data[80];
spis *next;
};
spis *head, *p, *tail;
head= new spis;
strcpy(head->data, “Иванов");
p= new spis;
72

head->next=p;
strcpy(p->data, "Петров");
tail= new spis;
p->next=tail;
tail->next=0;
strcpy(tail->data, "Сидоров");
for(p=head; p; p=p->next)
printf("%s\n",p->data);
операции при работе со списками
p=head; //переход к голове списка
p=p->next; //переход к следующему элементу списка
операции вставки элементов
в начало списка
p = new spis;
p->next=head;
head=p;
в середину списка (после элемента pp)
pp=head->next->next;
p = new spis;
p->next=pp->next;
pp->next=p;
в конец списка (по сути то же что и в середину)
p = new spis;
p->next=NULL;// или p->next=tail->next;
tail->next=p;
tail=p;
операции удаления элементов
удаление головного элемента
p=head;
head=head->next;
delete(p);
удаление неголовного элемента (после pp)
p=pp->next;
pp->next=p->next; (pp->next=pp->next->next;)
если нужен хвост if(p==tail) tail=pp;
delete(p);
73

Так как операции с головным элементом отличаются от остальных,
информационные поля первого элемента иногда удобно не использовать.
Стек и очередь – вставка разрешена только в голову или хвост списка.
Если вставлять элементы в голову списка – получим стек, если в хвост –
очередь.
Пример создания стека
#include <stdio.h>
struct spis {
char data[80];
spis *next;
} *head, *p;
main() {
int flag;
head=new spis; head->next=NULL;
do {
p=new spis;
scanf("%s",p->data);
p->next=head->next;
head->next=p;
printf("добавить еще элемент? 1/0 "); scanf("%d",&flag);
} while(flag);
for(p=head->next; p; p=p->next)
printf("%s\n",p->data);}
Пример создания очереди
#include <stdio.h>
struct spis {
char data[80];
spis *next;
} *head, *p;
main() {
int flag;
head=tail=new spis; tail->next=NULL;
do {
p=new spis;
scanf("%s",p->data);
tail->next=p;
tail=p;
printf("1/0 "); scanf("%d",&flag);
} while(flag);
tail->next=NULL;
for(p=head->next; p; p=p->next)
printf("%s\n",p->data);}
74

Пример поиска в списке строки st
for(flag=1,p=head->next; p; p=p->next)
if(strcmp(p->data, st)==0) {printf("%s\n",p->data);flag=0;}
if(flag) printf(“не найдено”);
Пример сортировки элементов списка (меняются только ссылки,
информационные поля не копируются!)
do {
f=0;
for(p=head; p->next->next; p=p->next) {
t1=p->next; t2=t1->next;
if(strcmp(t1->data, t2->data) > 0) {
p->next=t2; t1->next=t2->next; t2->next=t1; f=1;
}
}
} while(f);
Вставка в отсортированный список
spis * findplace (spis * p, char st[]){ // функция поиска места для вставки
while (p->next && (strcmp(p->next->data, st) < 0))
p = p->next;
return p;
}
int main(){
spis *head, *p, *t;
int flag;
head=new spis, head->next=NULL;
do{
p=new spis;
scanf("%s",p->data);
t=findplace(head, p->data);
p->next=t->next;
t->next=p;
printf("1/0 "); scanf("%d",&flag);
} while(flag);
for(p=head->next; p; p=p->next)
printf("%s\n",p->data);
}
Удаление списка.
for(p=head; p; p=pp) {
pp=p->next;
75

printf("%s\n",p->data);
delete(p);
}
Двусвязные списки
Список, в котором элемент имеет ссылки на предыдущий и последующий
элемент называется двусвязным. Главное отличие от односвязного –
возможность перемещения в обоих направлениях.
struct spis {
char data[80];
spis *next, *prev;
};
Вставка и удаление элементов
вставка в голову
p=new spis;
p->next=head;
p->prev=NULL;
head->prev=p
head=p;
вставка хвост
p=new spis;
p->next=NULL;
p->prev=tail;
tail->next=p
tail=p;
вставляем р после t
p= new spis;
p->next=t->next;
p->perv=t;
t->next->prev=p;
t->next=p;
удаление элемента
p->next->prev=p->prev;
p->prev->next=p->next;
delete(p)
76

удаление головного элемента
p-next->prev=NULL;
head=p->next;
delete(p)
удаление последнего элемента
p->prev->next=NULL;
tail=p->prev;
delete(p);
перемещение по списку (поиск головы или хвоста)
for(;p->prev; p=p->prev);
head=p;
for(;p->next; p=p->next);
tail=p;
вывод элементов
for(p=head; p; p=p->next)
printf("%s\n",p->data);
for(p=tail; p; p=p->prev)
printf("%s\n",p->data);
заполнение списка (добавление в хвост)
head=tail=new spis, head->next=head->prev=NULL;
do{
p=new spis;
scanf("%s",p->data);
p->next=NULL;
p->prev=tail;
tail->next=p;
tail=p;
} while(strcmp(p->data,"q"));
Циклический список
Иногда список (односвязный или двусвязный) замыкают в кольцо, то есть
указатель next последнего элемента указывает на первый элемент, и (для
двусвязных списков) указатель prev первого элемента указывает на последний.
Головой можно считать любой элемент.
Пример
#include <stdio.h>
77

#include <string.h>
struct spis {
int data;
spis *next;
} *head, *p,*tail;
main() {
int flag=0; int x=1;
head=tail=new spis; head->next=tail; head->data=x++;
do { //создаем циклический список из 10 элементов
p=new spis;
p->data=x++;
tail->next=p;
p->next=head;
tail=p;
} while(x<=10);
for(int i=0; i<7; i++) { выводим 7 раз список, изменяя головной элемент
p=head;
do {
printf("%4d ",p->data);
p=p->next;
} while(p!=head);
puts("");
head->data=x++;
head=head->next;
}
}
Контрольные вопросы
Какие виды списков вы знаете?
Преимущества и недостатки списков.
Как можно вставить элемент в середину списка?
78

11. Файловый ввод-вывод в языке Си
Для удобства работы с информацией на запоминающих устройствах она
хранится в виде файлов. Файл – поименованная область данных на внешнем
носителе.
В языке Си для работы с файлами можно использовать стандартную
библиотеку ввода-вывода <stdio.h>. При открытии файл связывается со
стандартной структурой FILE, содержащей необходимую информацию о нем.
Открытие файла
FILE * fopen(char name[], char t[])
name – имя файла на диске
t – тип открытия файла.
name – “a.txt” “b.dat” “c:\\temp\\ip811\\a.txt”
тип – все файлы делятся на текстовые и двоичные.
t – текстовый
b – двоичный
файлы можно открывать существующие или создавать новые
r – открытие существующего (чтение)
w – создание нового (запись)
а – открытие существующего для добавления
+ – разрешает добавлять к файлам, открытым на чтение и читать из файлов
открытых на запись
“r” “rb” “rt” “rb+” “wb” “a”
если в процессе открытия произошла ошибка, то возвращается NULL значение.
#include <stdio.h>
main(){
FILE *f;
if ((f=fopen("a.txt","r")) == NULL) {
printf("Error open file a.txt ");
getch();
exit(1);
}
}
закрытие файла
fclose(FILE *);
Разрывается связь файловой переменной с файлом на диске, если в буфере
ввода-вывода оставались данные они записываются в файл.
проверка на окончание файла
feof(FILE *)
79

Возвращает ненулевое значение, если обнаружен признак конца файла,
иначе –ноль.
ввод-вывод для текстовых файлов
int fgetc(FILE *)
считывает один символ из файла, при окончании файла возвращается код
символа = -1 (EOF)
Частая ошибка:
do{
ch=fgetc(f);
printf("%c",ch);
} while(ch!=EOF)
В этом случае после окончания файла считывается лишний символ EOF и он
выводится на экран.
Пример правильного использования:
while((ch=fgetc(f))!=EOF) {
printf("%c",ch);
}
char * fgets(st,N,f)
считывает в строку st из файла f не более N символов. Возвращает указатель на
считанную строку. (NULL, если не считано.)
Пример:
while (fgets(st,80,f)) {
printf("%s",st);
}
fputc(char, FILE *)
вывод в файл символа
Пример:
for (i=0; i<26; i++)
fputc(i+'a',f);
fputs(char[], FILE *)
Вывод в файл строки
все эти функции имеют аналоги для работы с консолью
char ch, st[80];
ch=getchar();
gets(st);
putchar(ch);
puts(st);
80
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
