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

Программирование на языке Си. Учебное пособие для СПО

.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]