- •1.Учебно-методический комплекс дисциплины
- •Данные о преподавателе:
- •1.2 Данные о дисциплине:
- •Выписка из учебного плана
- •1.3 Пререквизиты
- •1.4 Постреквизиты
- •1.5 Цели и задачи дисциплины
- •1.6 Перечень и виды заданий и график их выполнения
- •Виды заданий и сроки их выполнения
- •1.7 Список литературы
- •1.8 Контроль и оценка знаний.
- •1.9 Политика и процедура курса
- •2 Содержание Активного раздаточного материала
- •2,1 Тематический план курса
- •2.2 Конспект лекционных занятий
- •#Define millenium 1000
- •// Нельзя изменять
- •/* Это моя первая программа на Си*/
- •Void main()
- •Void main ()
- •Void main()
- •Void main()
- •Void main()
- •Void main()
- •Void main(void)
- •If (логическое выражение) оператор 1;
- •Void main()
- •If (логическое выражение)
- •If (условие 1 )
- •If (условие 1 )
- •If (X) printf ("Число не равно нулю");
- •Void main()
- •Void main(void)
- •Void main()
- •Void main()
- •Int n; // Количество узлов интерполяции
- •Int k; // Номер узла
- •Void main()
- •Vvedi 5 7
- •Int *a; // Указатель на массив целых чисел
- •Void main()
- •Int vect[10], s1[50];
- •Int vect [n];
- •Void main( )
- •Int min; // номер минимального элемента
- •Int random ( int n )
- •Void lin(char a)
- •Void z1(int * X, int * y)
- •Void z1(int*, int*);
- •Int имя переменной;
- •Void main()
- •8 Есть цифра
- •8 Есть буква или цифра
- •Int integ;
- •Void main()
- •Void main()
- •Void main()
- •Void main()
- •Int ocen;
- •Void main( )
- •File *указатель на файл;
- •Fclose(имя_указателя);
- •Void main()
- •Int h; /*дескриптор создаваемого файла*/
- •Void main()
- •Initgraph(&g_driver,&g_mode," ");
- •Vgamed 1 640x350
- •Vgahi 2 640x480
- •Initwindow ( 400, 300 ); // открыть окно для графики 400 на 300
- •Void main(void)
- •Int g_driver,g_mode,g_error;
- •2.3 Планы лабораторных занятий
- •Планы занятий в рамках самостоятельной работы студентов под руководством преподавателя (срсп).
- •2.5 Планы занятий в рамках самостоятельной работы студентов (срс).
- •2.6 Тестовые задания для самоконтроля с указанием ключей
- •Глоссарий
Int ocen;
}ball;
struct vuz
{ struct ses ball;
char fam[25];
char grup[10];
int kod;
}stud 1,stud 2;
stud 1. ball. dis=”физика”;
stud 1. ball. ocen =5;
Задача
По группе Инф.-09-01р даны результаты первой аттестации и контрольной работы по дисциплине «ЯиТП». Необходимо определить средний балл по каждому студенту и вывести все данные на экран.
Исходные данные:
Адактилос А. 91 80
Динисламова А. 90 80
Худайбергенова Назым 99 80
Нуркиев Еркебулан 60 0
Молдакынов Абзал 80 0
Кенжебеков Адильбек 60 50
Ниже приведена программа, которая решает данную задачу
#include <stdio.h>
Void main( )
{ struct ved
{ char FAM[15];
int PA;
int KR;
float SB;
} Stud[6];
for(i=0,i<6,i++)
{ scanf(“%s%d%d”, Stud[i]1. FAM, &Stud[i]. PA, &Stud[i]. KR);
Stud[i]. SB= (Stud[i]. PA+ Stud[i]. KR)/2;
}
for(i=0,i<6,i++)
printf (“\n %s %d %d %f”, Stud[i]. FAM, Stud[i]. PA, Stud[i]. KR, Stud[i]. SB);
}
Cтруктуры и указатели
Мы уже упомянули, что нельзя передавать функции структурную переменную целиком в качестве аргумента (хотя можно передавать отдельные элементы). Но существует возможность обойти это ограничение, используя в качестве аргумента указатель на структуру. Описание
struct anketa *uk;
говорит , что uk - указатель на структуру типа anketa. Обозначение относится к конкретному элементу структуры и означает выборку этого элемента, например: uk-> tab_nom. Поскольку uk есть указатель на структуру anketa, то к элементу tab_nom можно обращаться и так:
(*uk).tab_nom,
если учесть, что указатель установлен на начало массива структур. Имя массива, как обычно, эквивалентно адресу его начального элемента и при добавлении к указателю на структуру или вычитании из него целого числа размер структуры учитывается автоматически Так, оператор uk=a; устанавливает указатель на первый экземпляр массива структур, а запись ++a; обеспечивает автоматический переход к следующему экземпляру. В выражении (*uk).fio скобки обязательны, так как приоритет операции выделения элемента " . " выше чем у "*".
Основная литература: 2[165-179]
Дополнительная литература:1,2,3,5
Контрольные вопросы:
1.Какие действия включает в себя работа со структурами ?
2. Как определяется структура?
3. Каким образом производится доступ к отдельным элементам структуры?
4. Какие способы создания экземпляров структуры существуют?
5. Какие действия над структурами существуют?
Лекция 12. Указатели иа структуры . Указатели на структуры
Структуры со ссылками на себя
Указатели на структуры
Для иллюстрации некоторых моментов, касающихся указателей на структуры и массивов структур приведем программу подсчета ключевых слов текста Си – программы, пользуясь указателями для получения элементов массива.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXWORD 100
int getword (char*, int);
struct key * binsearch (char*, struct key *, int);
\\ подсчет ключевых слов Си
main ( )
{ char word [MAXWORD];
struct key *p;
while (getword (word, MAXWORD)!=EOF)
if (isalpha(word[0]))
if ((p=binsearch(word, keytab, NKEYS))!=NULL)
p->count++;
for (p=keytab; p<keytab+NKEYS; p++)
if (p->count > 0)
printf (“%4d%s\n”,p->count, p->word);
return 0;
}
// binsearch: найти слово в tab[0]…tab[n-1]
struct key * binsearch (char *word, struct key *tab, int n)
{ int cond;
struct key *low=&tab[0];
struct key * high = &tab[n];
struct key *mid;
while (low<high);
{ mid=low+(high – low)/2;
if ((cond=strcmp (word,mid->word))<0)
high=mid;
else if (cond > 0)
low=mid+1;
else
return mid;
}
return NULL;
}
Если функция binsearch находит ключевое слово, то она выдает указатель на него, в противном случае она возвращает NULL. К элементам массива keytab доступ осуществляется через указатель. Инициализаторами для low и high служат указатели на начало и на место сразу после конца массива. Вычисление положения среднего элемента с помощью формулы
mid = (low +high)/2
не годится, поскольку указатели нельзя складывать. Однако к ним можно применить операцию вычитания, и так как high-low есть число элементов, присваивание
mid = low + ( high – low )/2
установит в mid указатель на элемент, лежащий посередине между low и high.
В цикле for оператор p++ увеличит р на такую величину, чтобы выйти на следующий структурный элемент массива. Не следует полагать, что размер структуры равен сумме размеров ее членов. Оператор sizeof возвращает правильное значение. Функция getword принимает следующее слово или литеру из входного потока. Под словом понимается цепочка букв – цифр, начинающаяся с буквы, или отдельная непробельная литера. По концу файла функция выдает EOF, в остальных случаях ее значением является код первой литеры слова или код отдельной литеры, если она не буква.
\\ getword: принимает следующее слово или литеру
int getword (char * word, int lim)
{ int c, getch (void);
void ungetch (int);
char * w =word;
while (isspace (c=getch( ))
;
if (c!=EOF) *w++=c;
if (!isalpha(c))
{ *w=’\0’;
return c;
}
for (; --lim >0; w++)
if (!isalnum (*w=getch( )))
{ ungetch (*w);
break;
}
*w=’\0’;
return word [0];
}
Обращение к ungetch позволяет вернуть лишнюю литеру во входной поток.В getword используются также isspace – для пропуска пробельных литер, isalpha – для идентификации букв и isalnum – для распознавания букв – цифр. Все они описаны в стандартном головном файле <ctype.h>. Как работают функции getch и ungetch? Во многих случаях программа не может «сообразить», прочла ли она все, что требуется, пока не прочтет лишнего. Это означает, что программа прочла на одну литеру больше, чем нужно, а именно литеру, которую нельзя включать в число. Поэтому необходима операция, которая возвращала бы лишнюю литеру. Механизм обратной посылки литеры легко моделируется с помощью пары согласованных друг с другом функций, из которых getch поставляет очередную литеру из ввода, а ungetch отправляет назад литеру во входной поток, так что при следующем обращении к getch мы вновь ее получим.
Структуры со ссылками на себя
Пусть мы хотим решить более общую задачу – написать программу, подсчитывающую частоту встречаемости для любых слов входного потока. Так как список слов заранее не известен, мы не можем предварительно упорядочить его и применить, например, бинарный поиск, так как цель сортировки – облегчить поиск элементов в таком упорядоченном множестве. Было бы неразумно пользоваться и линейным поиском каждого полученного слова, чтобы определять, встречалось оно ранее или нет – в этом случае программа работала бы слишком медленно. Поэтому встает вопрос – как можно организовывать данные, чтобы эффективно справляться со списком произвольных слов?
Один из способов - постоянно поддерживать упорядоченность уже полученных слов, помещая каждое новое слово в такое место, чтобы не нарушалась имеющаяся упорядоченность. Для этого лучше всего подходит структура данных, называемая бинарное дерево.
В дереве на каждое отдельное слово предусмотрен «узел», который содержит:
-указатель на текст слова;
-счетчик числа встречаемости;
-указатель на левый сыновний узел;
-указатель на правый сыновний узел;
У каждого узла может быть один или два сына, или узел вообще может не иметь сыновей. Узел, не имеющий преемника, называется листом.
Узлы в дереве располагаются так, что по отношению к любому узлу левое поддерево содержит только те слова, которые лексикографически меньше, чем слово данного узла, а правое – слова, которые больше него. Процесс поиска в дереве по сути рекурсивен, так как поиск в любом узле использует результат поиска в одном из своих сыновних узлов. В соответствии с этим для добавления узла и печати дерева здесь наиболее естественно применить рекурсивные функции.
Описание узла бинарного дерева удобно представить в виде структуры с четырьмя компонентами:
struct tnode //узел дерева
{ char *word; //указатель на текст
int count; //число вхождений
struct tnode*left; //левый сын
struct tnode*right; // правый сын
};
Структура не может включать саму себя, но ведь
struct tnode*left;
определяет left как указатель на узел tnode, а не сам tnode.
Иногда возникает потребность во взаимоссылающихся структурах: двух структурах, ссылающихся друг на друга.
Прием, позволяющий справляться с этой задачей, демонстрирует следующий фрагмент:
struct t
{ …
struct s *p; // p указывает на s
};
struct s
{ …
struct t *q; // q указывает на t
};
Главная программа нашей задачи читает слова с помощью функции getword и вставляет их в дерево посредством функции addtree.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXWORD 100
struct tnode * addtree (struct tnode *, char *);
void treeprint (struct tnode *);
int getword ( char * , int );
// подсчет частоты встречаемости слов
main ()
{ struct tnode * root; // указатель на корневой узел
char word [MAXWORD];
root = NULL;
while ( getword(word, MAXWORD)!=EOF)
if ( isalpha (word[0]))
root = addtree(root,word);
treeprint(root);
return 0 ;
}
Функция addtree рекурсивна. Для каждого вновь поступившего слова ищется его копия и в счетчик добавляется 1 , либо для него заводится новый узел, если такое слово в дереве отсутствует. Создание нового узла сопровождается тем, что addtree возвращает на него указатель, который вставляется в узел родителя.
struct tnode * talloc ( void);
char * strdup ( char *);
// addtree: добавляет узел со словом w в p или ниже него
struct tnode * addtree ( struct tnode *p, char * w)
{ int cond;
if ( p= = NULL) //слово встречается впервые
{ p=talloc(); // создается новый узел
p->word = strdup (w);
p->count =1;
p->left = p-> right = NULL;
}
else if (( cond = strcmp( w,p->word))= = 0)
p->count + + ; // это слово уже встречалось
else if ( cond<0) // корня < левого поддерева
p->eft=addtree( p->left,w);
else
p->right=addtree( p->right,w);
return p;
}
Память для нового узла запрашивается с помощью программы talloc, которая возвращает указатель на свободное пространство памяти, достаточное для хранения одного узла дерева, а копирование нового слова в отдельное место памяти осуществляется с помощью strdup.
# include <stdlib.h>
// talloc : создает tnode
struct tnode * talloc ( void)
{ return ( struct tnode * ) malloc ( sizeof (struct tnode));
}
// strdup: копирует стринг, указанный в аргументе,
//в место , полученное с помощью malloc
char * strdup ( char*s)
{ char *p;
p=(char*) malloc ( strlen (s) + 1); // +1для ‘\0’
if (p!=NULL) // если память выделена
strcpy (p,s);
return p;
}
Функция treeprint печатает дерево в лексикографическом порядке; для каждого узла она печатает его левое поддерево ( все слова, которые меньше слова данного узла) , затем само слово и, наконец, правое поддерево ( все слова , которые больше слова данного узла).
// treeprint: упорядоченная печать дерева p
void treeprint (struct tnode * p)
{ if (p!=NULL) //в дереве имеется хотя бы один узел
{ treeprint ( p->left);
printf ( “%4d%s\n”, p->count, p->word);
treeprint (p->right);
}
}
Основная литература – 1[135-138].
Контрольные вопросы:
Какова оптимальная, с точки зрения эффективности обработки списков произвольных слов, структура данных, и почему?
Может ли структура включать саму себя? А какой выход в случае необходимости такого включения?
Как организовать отношение зависимости между двумя структурами?
В чем смысл рекурсии и как она реализована в функциях addtree и treeprint?
Объясните назначение и работу функции malloc.
Лекция 13. Файлы. Режим последовательного доступа. Режим прямого доступа.
Файл – это набор данных, размещенный на внешнем носителе и рассматриваемый в процессе обработки и пересылке как единое целое.
Прежде, чем начать работать с файлом, его нужно открыть для доступа, т.е. создать и инициализировать область данных, которая содержит информацию о файле: имя, путь и т.д.
В алгоритмическом языке «С» это делает функция fopen. Она связывает физический файл на носителе, например B:\LR7.CPP, с логическим именем в программе. Логическое имя – это указатель на файл, т.е. на область памяти, где храниться информация о файле. Указатели на файлы необходимо объявлять. Формат объявления такого указателя следующий:
