учебно-методический комплекс
ПРОГРАММИРОВАНИЯ
Красноярск
ИПК СФУ
2008
УДК 004.438
ББК 32.973
Н73
Электронный учебно-методический комплекс по дисциплине «Языки программи-
рования» подготовлен в рамках инновационной образовательной программы «Ин-
форматизация и автоматизированные системы управления», реализованной в ФГОУ
ВПО СФУ в 2007 г.
Рецензенты:
Красноярский краевой фонд науки;
Экспертная комиссия СФУ по подготовке учебно-методических комплексов дис-
циплин
Новиков, Е. А.
Н73 Языки программирования. Язык С. Версия 1.0 [Электронный ресурс] : кон-
спект лекций / Е. А. Новиков, Ю. А. Шитов. – Электрон. дан. (3 Мб). – Красно-
ярск : ИПК СФУ, 2008. – (Языки программирования : УМКД № 147-2007 / рук.
творч. коллектива Ю. А. Шитов). – 1 электрон. опт. диск (DVD). – Систем. тре-
бования : Intel Pentium (или аналогичный процессор других производителей)
1 ГГц ; 512 Мб оперативной памяти ; 3 Мб свободного дискового пространст-
ва ; привод DVD ; операционная система Microsoft Windows 2000 SP 4 /
XP SP 2 / Vista (32 бит) ; Adobe Reader 7.0 (или аналогичный продукт для чте-
ния файлов формата pdf).
ISBN 978-5-7638-1250-3 (комплекса)
ISBN 978-5-7638-1459-0 (конспекта лекций)
Номер гос. регистрации в ФГУП НТЦ «Информрегистр» 0320802545
от 02.12.2008 г. (комплекса)
Настоящее издание является частью электронного учебно-методического ком-
плекса по дисциплине «Языки программирования», включающего учебную програм-
му, конспект лекций «Языки программирования. Ассемблер», методические указания
по лабораторным работам, методические указания по самостоятельной работе, кон-
трольно-измерительные материалы «Языки программирования. Банк тестовых зада-
ний», наглядное пособие «Языки программирования. Презентационные материалы».
Приведены теоретические сведения по языку С, его типам и структурам данных,
рассмотрены типовые задачи программирования.
Предназначен для студентов направления подготовки специалистов 090102.65
«Компьютерная безопасность» укрупненной группы 090000 «Информационная безо-
пасность».
Рекомендовано к изданию
Инновационно-методическим управлением СФУ
Редактор Т. И. Тайгина
Разработка и оформление электронного образовательного ресурса: Центр технологий элек-
тронного обучения информационно-аналитического департамента СФУ; лаборатория по разработке
мультимедийных электронных образовательных ресурсов при КрЦНИТ
Содержимое ресурса охраняется законом об авторском праве. Несанкционированное копирование и использование данного про-
дукта запрещается. Встречающиеся названия программного обеспечения, изделий, устройств или систем могут являться зарегистрирован-
ными товарными знаками тех или иных фирм.
ВВЕДЕНИЕ ................................................................. 8
ЛЕКЦИЯ 1. ОСНОВНЫЕ УСТРОЙСТВА ЭВМ И ИХ
НАЗНАЧЕНИЕ. ИСТОРИЯ РАЗВИТИЯ ЯЗЫКОВ ........ 9
1. Введение .......................................................................................................................... 9
2. Основные устройства ЭВМ и их назначение .......................................................... 10
3. История развития языков ........................................................................................... 11
ЛЕКЦИЯ 2. ОБЩИЕ ПРИНЦИПЫ ПОСТРОЕНИЯ
ЯЗЫКОВ ПРОГРАММИРОВАНИЯ. ПРЕПРОЦЕССОР
И МАКРООБРАБОТКА. ЭТАПЫ РЕШЕНИЯ ЗАДАЧ
НА КОМПЬЮТЕРЕ .................................................... 15
1. Общие принципы построения языков программирования ................................. 15
2. Препроцессор и макрообработка. ............................................................................. 18
3. Этапы решения задач на компьютере ...................................................................... 19
ЛЕКЦИЯ 3. СОВРЕМЕННЫЕ ИНТЕГРИРОВАННЫЕ
СРЕДЫ. ВСТРОЕННЫЙ ОТЛАДЧИК.
БИБЛИОТЕКА ПРОГРАММ И КЛАССОВ .................. 21
1. Современные интегрированные среды .................................................................. 21
2. Встроенный отладчик ................................................................................................. 22
3. Библиотеки программ и классов ............................................................................... 23
ЛЕКЦИЯ 4. ПРОСТЕЙШАЯ ПРОГРАММА. ВЫВОД
ТЕКСТА НА ЭКРАН. ДИРЕКТИВЫ CLRSCR()
И GETCH() ................................................................ 26
1. Простейшая программа ............................................................................................... 26
2. Вывод текста на экран ................................................................................................ 28
3. Препроцессор ................................................................................................................ 29
4. Директивы clrscr() и getch() ........................................................................................ 31
ЛЕКЦИЯ 5. ПАМЯТЬ. ПЕРЕМЕННЫЕ. ВЫВОД НА
ЭКРАН. ЗАПИСЬ В ПЕРЕМЕННЫЕ ТИПА INT
И FLOAT. ВВОД С КЛАВИАТУРЫ ............................ 35
1. Память ............................................................................................................................ 35
2. Переменные ................................................................................................................... 35
3. Вывод переменных на экран ..................................................................................... 36
4. Запись в переменные типа int и float........................................................................ 37
5. Ввод с клавиатуры....................................................................................................... 40
План
1. Введение.
2. Основные устройства ЭВМ и их назначение.
3. История развития языков.
1. Введение
План
2. Препроцессор и макрообработка.
2. Встроенный отладчик
План
1. Простейшая программа.
2. Вывод текста на экран.
3. Препроцессор.
4. Директивы clrscr() и getch().
1. Простейшая программа
void main()
{
}
void main()
{ }
или
void main () { }
void main();
{
}
void main
{
}
void main()
{
}.
Поясним, почему коды представленных программ неправильны.
В программе 1 в слове Void используется заглавная буква V, в программе 2
после директивы void main() поставлена точка с запятой, в программе 3 после
слова main нет скобок, в программе 4 после директивы закрывающая
фигурная скобка поставлена точка.
Замечание 2. Все написанные директивы программы выполняются
компьютером по порядку начиная с первой.
Замечание 3. Директивы открывающая фигурная скобка и
закрывающая фигурная скобка могут использоваться в программе
произвольное количество раз. Программу-пустышку, например, можно
записать так:
void main()
{ { { } } }
или
void main()
{ {} {} {} }
3. Препроцессор
Как только эта программа начнет работать, код clrscr() удалит с экрана
выдачи результатов расчетов всю информацию, которая находилась там до
последнего запуска программы. Поэтому, сколько бы раз программа «Доброе
утро» ни запускалась, на экране выдачи результата будет сохраняться только
одно сообщение:
Доброе утро, студент!!!
То есть будет сохраняться результат последней работы программы.
Любую директиву в программе можно использовать многократно.
Применение той или иной директивы диктуется только условием задачи.
Программа 5
#include <conio.h>
#include <stdio.h>
void main()
{
clrscr();
clrscr();
clrscr();
getch();
getch();
clrscr();
clrscr();
getch();
getch();
}
План
1. Память.
2. Переменные.
3. Вывод переменных на экран.
4. Запись в переменные типа int и float.
5. Ввод с клавиатуры.
1. Память
Рис. 5.1
2. Переменные
int a, b, c;
Целое число в n1 =
Целое число в n1 =
Для записи информации (чисел) в переменные типа int или float можно
использовать оператор присваивания, который определяется символом =
(равно). Например, если код int k1; резервирует два байта памяти под именем
k1 для записи целых чисел, то код k1 =2; записывает (засылает) в эту область
памяти число 2.
Переменная может быть определена при ее объявлении. Например,
возможен в программе код float r1 = 0.8, we = –3.5; При выполнении
данного кода компьютер выделяет две области памяти с именами r1, we и
одновременно занесет в эту память соответственно числа 0.8 и –3.5.
Программа 1
#include <conio.h>
#include <stdio.h>
#include <iostream.h>
void main()
{ int a, b, c, z; // Резервируется память для четырех целых чисел.
clrscr(); // Удаляется с экрана выдачи информация.
cout << “Эта программа резервирует память для четырех целых чисел\n”;
cout << “и засылает в переменные a, b и c”
<< “ соответственно числа 2, 5, 7\n”;
a = 2; b = 5; c = 7;
cout << “В память введена следующая информация:\n”;
cout << “a = “ << a << “\nb = “ << b << “\nc = “ << c << “\n”;
getch(); } // Ожидается нажатие клавиши.
Значение чисел Xa, Xb, Xc, Xz, которые вывел компьютер, заранее
невозможно определить. Поэтому программист должен сам заботиться об
инициализации заказанной памяти.
Замечание. Переменную можно не инициализировать, если она
используется после того, как ей присвоили какой-либо результат расчета.
Здесь N1, N2, N3, N4 – номера адресов ячеек памяти, которые компьютер
выделил программе для записи целых чисел.
Если надо выдать адреса в десятичной системе исчисления, следует
перед &n1, &n2, &n3 и &n4 поставить директиву преобразования типа
(unsigned int). Скобки в данном случае являются элементами директивы.
Cоставим программу для простой, но очень важной задачи, – поменять
местами значения переменных x и y.
Следующая программа переставляет числа, которые записаны в
переменных x и y.
Программа 4
#include <conio.h>
#include <stdio.h>
#include <iostream.h>
void main()
{ float x = -2.5, y = 5.1, z;
cout << “Значение в x= ” << x << “\nЗначение y= “ << y << “\n”;
z = x; // Пересылается информация из x в z.
x = y; // Пересылается информация из y в x.
y = z; // Пересылается информация из z в y.
cout << “\n Результат работы программы: \n\n”;
5. Ввод с клавиатуры
scanf(“%d”,&c);
cout << “Вы ввели числа: \n”;
cout << “a = “ << a << “\nb = “ << b << “\nc = “ << c << “\n”;
getch(); } // Ожидается нажатие клавиши.
Управляющая строка каждой директивы scanf программы имеет вид
“%d”, что означает ввод целого числа. При работе данной программы будут
происходить запросы на ввод целых чисел. После вывода на экран
очередного сообщения компьютер не будет выполнять программу до тех пор,
пока на клавиатуре не будет набрано число и не нажата клавиша Enter.
Целое число должно соответствовать диапазону представления переменных
типа int.
Приведем код программы, которая демонстрирует ввод трех целых
чисел с клавиатуры с применением директивы scanf(“%d%d%d”, &a,
&b, &c).
Договоримся в коде программ не писать директивы препроцессора,
присутствие которых, вообще говоря, обязательно. Но формат этих директив
во всех программах одинаковый и добавить в случае необходимости эти
директивы к тексту программы не представляет труда.
Следующая программа вводит три целых числа в память компьютера
через клавиатуру.
Программа 8
void main()
{int a, b, c, z; // Резервируется память для четырех целых чисел.
clrscr(); // Очищается экран.
cout << “Введи с клавиатуры три целых числа a, b и c,\n”
<< “Ннабери три числа, разделенных пробелом, и нажми Enter ”;
scanf(“%d%d%d”, &a, &b, &c);
cout << “Вы ввели числа: \n”;
cout << “a = “ << a << “\nb = “ << b << “\nc = “ << c << “\n”;
getch(); } // Ожидается нажатие клавиши.
План
1. Арифметические операции.
2. Математические выражения и функции.
1. Арифметические операции
Прокомментируем директиву
cout << “a = “ << a << “\nb = “ << b << “\nc = “ << c << “\n”;
Программа 5
void main()
{ int a = 2, b = 5, c = 7, z; // Резервируется память для 4 целых чисел.
// При определении переменных в память
// засылаются начальные данные.
clrscr(); // Удаляется с экрана выдачи информация.
cout << “Эта программа вычисляет значение z = a + b * c “
z = a + b * c;
⎛ ⎞
⎜ sin 2 x + cos 2 y ⎟
Z =⎜ −e|cos x|+ sin y
⎟ ⋅ ln(| x | +1) − x 2 + 1 .
⎜ sin ⎛ x + y ⎞ + 1,5 ⎟
⎜ ⎜ 2 ⎟ ⎟
⎝ ⎝ ⎠ ⎠
z = fabs(cos(x)) + sin(y);
z = exp(z);
z1= sin(x) * sin(x) +cos(y) * cos(y);
z1 = z1/(sin((x + y)/2.) + 1.5);
z = z1 - z; // Вычисляем для задачи 2 значение
// выражения в скобках.
z = z * log(fabs(x) + 1) - sqrt(x*x + 1); // Результат решения задачи.
cout << “\nРезультат вычисления z = “ << z << “\n”;
getch();
}
План
Логические операции:
операция && – логическое И;
операция || – логическое ИЛИ;
операция ! – логическое НЕ.
При помощи этих операций можно составить выражение, которое
одновременно проверяет несколько условий. Например, если x1, x2, x3, x4 и
x5 – отметки студента по пяти предметам, то студент будет отличником тогда
и только тогда, когда значение выражения
(x1 == 5) && (x2 == 5) && (x3 == 5) && (x4==5) && (x5 ==5)
2. Условные операторы
if (A) { …… }; S1,
else
{ if (b>c)
cout << “max= “ << b << “\n”;
else
cout << “max= “ << c << “\n”; } getch(); }
switch (<выражение>)
{
case C1:
S1
break;
case C2:
S2
break;
…
case CK:
SK
break;
default:
SD
break;
}
SS;
break;
default : cout << "Команда меню с номером" << k
<< "отсутствует\n";
}}
goto L ;
План
1. Цикл for.
2. Цикл while.
3. Цикл do – while.
1. Цикл for
-10 + 0 = -10
-9 + 1 = -8
-8 + 2 = -6
-7 + 3 = -4
-6 + 4 = -2
-5 +5 = 0
Работа цикла завершена
2. Цикл while
while ( <условие>) { W; }
3. Цикл do – while.
do
{ W; }
while ( <условие>)
Здесь <условие> – любое выражение, допустимое в языке С; W –
последовательность операторов.
<условие> принимает значение ИСТИНА, если значение выражения
не равно нулю, ЛОЖЬ – в противном случае. Тело цикла выполняется, если
<условие> принимает значение ИСТИНА.
y = x;
do
{y1 = y; // В y1 засылается значение y с предыдущего шага.
…
y = f(y); // На текущем шаге y определяется по формуле.
} while (fabs(y - y1) > ep); // Выполнять цикл до заданной точности.
План
1. Массивы.
2. Некоторые простейшие задачи.
3. Матрицы.
4. Перебор элементов матрицы.
1. Массивы
a(0) = X0
a(1) = X1
a(2) = X2
a(3) = X3
Здесь X0, X1, X2, X3 – какие-то случайные числа. Данная программа демон-
стрирует, что задание начальных значений (инициализация) элементов
массива должна быть предусмотрена в программе. Инициализировать
элементы массива можно при его объявлении, например, используя код int
x[6] = {n1, n2, n3, n4, n5, n6}, здесь n1, n2, …, n6 некоторые числа. Если при
объявлении массива определить его первый элемент (int x[6] = {n1}), то
остальные элементы массива автоматически заполняются нулями.
Программа 6
void main(void)
{
const n = 4 ;
int i, a[n], max; // max – для максимального элемента массива.
clrscr();
cout << "Введи элементы массива с клавиатуры \n";
for (i = 0; i < n; i++)
{
printf("a[ %d ]= ", i);
scanf( " %d", &a[i] );
}
max = a[0]; // Первый элемент массива объявляем максимальным.
for (i = 1; i < n; i++) // В цикле перебираем индексы элементов
// массива.
if (max < a[i] ) max = a[i]; // Если максимальный меньше текущего
Программа 9
int main(void)
{ const n = 4 ;
int i, b, a[n] ;
Программа 10
void main(void)
{
const n = 7 ;
int j, i, b, k, a[n] ; // i, j – для текущих индексов массива; k – для числа
// сдвигов влево; b – для промежуточных результатов.
clrscr();
cout << "Введи элементы массива с клавиатуры \n";
for (i = 0; i <= n – 1; i++) // Начало цикла. Перебор индексов массива.
{
printf("a[ %d ]= ", i); // Подсказка для пользователя.
scanf("%d", &a[i]); // Ввод элемента массива с клавиатуры.
}
cout << "Введи число позиций, на которое надо сдвинуть массив влево, k = ";
cin >> k;
for (j = 0; j < k; j++)
{
b = a[0]; // Запоминаем первый элемент массива.
Программа 11
void main()
{ const n = 10;
FILE *f1;
int i, j, a[n];
int ma = 0, mt, im = 0;
clrscr();
cout << "Введи элементы массива с клавиатуры \n";
for (i = 0; i <= n - 1; i++) // Цикл. Перебор индексов массива.
{ printf("a[ %d ]= ", i); // Подсказка для пользователя.
scanf("%d", &a[i]); // Ввод элемента массива с клавиатуры.
}
cout << "\nВведен массив:\n\n";
for (i = 0; i < n; i++) // Вывод элементов массива на экран.
3. Матрицы.
a 11 a 12 a 13 . . . . . . . . a 1m
a 21 a 22 a 23 . . . . . . . . a 2 m
a 31 a32 a 33 . . . . . . . . .a 3m
....................
a n1 a n 2 a n3 . . . . . . . . a nm
Таблица 9.1
Координаты в памяти компьютера
а б в г
Рис. 9.1
План
1. Указатели.
2. Способы инициализации указателей.
3. Связь указателя с одномерным массивом.
4. Двумерный массив.
5. Динамическая память.
1. Указатели
<тип> *<имя>.
Например, коды (директивы) int *p; float *r; выделяют две области памяти
p и r размером два байта. При этом переменная p предназначена для записи
адресов переменных типа int, а переменная r предназначена для адресов
переменных типа float.
Программа 1. Объявляет указатель. Выдает случайную информацию
из области памяти, которая выделяется под указатель.
void main()
{ int *p; // Определяется переменная p, которая является указателем.
clrscr ();
cout << "В переменной p содержится случайный адрес: “
<< p << “\n”;
getch(); }
A A N
P *P
A A 6
P *P
Рис. 10.1
int *p, x;
p = &x;
A &X 222 A N
P X *P
Рис. 10.2
Рис. 10.3
A A N
P *P
Рис. 10.4
AN AN N A 8
P *P
Рис. 10.5
<тип> **<имя>
Например, коды (директивы) int **p; float **r; выделяют две области
памяти p и r размером два байта. При этом переменная p предназначена для
записи адресов указателей типа int, а переменная r предназначена для
адресов указателей типа float. Из определения указателя на указатель
следует, что если объявлены переменные p и a как переменные типа int **p
и int *a, то справедлив код p = &a. На схеме рис. 10.6 представлено
состояние памяти после выполнения кода int **p.
AY AY A A N
P *P **P
Рис. 10.6
4. Двумерный массив
Если задана матрица, например a[n][m], то для нее определен массив
указателей a[n]. В i-м элементе массива a[i] содержится адрес первого
элемента i-й строки. В переменной a содержится адрес первого элемента
матрицы, т. е. справедливо тождество &a[0][0] ≡ a.
Программа 9. Демонстрирует, что при объявлении двумерного массива,
определен массив указателей на первые элементы строк матрицы.
const n = 5, m =4;
void main()
{int b[n][m], i;
clrscr();
for (i = 0; i < n; i++) // Перебираем индексы строк двумерного
// массива.
{ cout << " b(" << i << ")= " << b[i]; // Вывод значений элементов
// одномерного массива.
// Вывод адресов первых элементов строк матрицы.
cout << " \n адрес b(" << i << ",0)= " << &b[i][0] << "\n\n";
} getch(); }
5. Динамическая память
Под динамической памятью понимается память, которая выделяется во
время работы программы. В языке С для выделения динамической памяти
используются указатели.
Одномерный массив. Для того чтобы задать память под одномерный
массив во время работы программы, необходимо:
определить указатель, например int *p;
задать требуемый размер n массива;
выполнить код p = new int [n].
Программа 11. Используется указатель для динамического выделения
памяти под одномерный массив.
void main()
{ int j,s;
int *data;
cout << "Введи размер массива ";
cin >> s; // Ввод размера массива.
data = new int[s]; // Выделяется динамическая память для элементов
// массива размерности s.
cout << “После инициализации в переменной data значение адреса = ”
<< data << “\n”;
cout << "\nРазмер массива = " << s
<< "\n\n Введи элементы массива с клавиатуры \n";
for (j = 0; j < s; j++)
{ cout << "data[" << j << "]= " ;
cin >> data [j]; // Доступ к элементам массива через индекс.
}
cout << "\nВведен массив: \n";
for ( j = 0; j < s; j++)
cout << data[j] << “ “; // Вывод элементов массива на экран.
cout << "\n";
delete [] data; // Удаляет память, которая использовалась для массива.
getch();
}
Замечание 2. Если в программе определен указатель, то в принципе без
предварительного выделения памяти можно использовать переменную с
индексом.
План
1. Файлы.
2. Символы.
3. Стандартные программы.
1. Файлы
Файл – это именованная область памяти на внешнем запоминающем
устройстве. Файлы служат для записи и хранения информации. Полное имя
файла для С имеет следующий формат:
<имя>.<тип>
Тип в имени может отсутствовать. Число символов в типе не
превышает трех. Для того чтобы в языке С во время работы программы
организовать ее взаимодействие с файлом на внешнем запоминающем
устройстве, надо:
объявить переменную, которая называется указателем файла;
открыть файл: связать указатель файла с конкретным именем файла;
закрыть файл, если программа завершила все операции с файлом на
внешнем носителе.
Формат объявления переменной, которая является указателем файла,
следующий:
FILE *<имя>;
Для связи файла на носителе с указателем в программе используется
функция fopen(). Код, который открывает файл, имеет следующий вид:
FILE *f1;
f1 = fopen(“<имя файла на носителе>”,”<режим доступа к файлу>”);
2. Символы
Для объявления символьных переменных в языке С используется
ключевое слово char. Этот тип переменных выделяет 1 байт для записи
char <имя>.
Замечание. Условный оператор if(!(ch > 'п' && ch < 'р')) в программе
используется потому, что между русскими буквами ‘п’ (код 175) и ‘р’ (код
224) находятся символы, которые не являются буквами русского алфавита.
Коды упомянутых символов начинаются от значения 176 и заканчиваются
значением 223. Оператор if(!(ch > 'п' && ch < 'р')) исключает вывод этих
символов на экран.
3. Стандартные программы
План
1. Строки.
2. Стандартные функции для работы со строками.
3. Массивы строк.
4. Доступ к функциональным клавишам.
1. Строки
Использовав символьные переменные, можно сформировать две
структуры данных: массив символов и строки. Массив есть множество
элементов одного типа. Строка – это массив символов, последним
элементом которого является символ ‘\0’ (обратный слеш, 0), называемый
концом строки. Принципиальная же разница между строкой и массивом
состоит в том, что присутствие в строке символа ‘\0’ неявно задает размер
массива. Форма объявления массива символов в языке С имеет следующий вид:
char <имя>[<размер>];
{
scanf("%s",a);
if (!feof(stdin))
cout << "!!!! " << a << "\n";
}
while (!feof(stdin));
}
!!!! Наша
!!!! Таня
!!!! громко
!!!! плачет
вводит в str всю строку, если число символов в строке меньше или равно k – 1).
При успешном завершении работы функции fgets() в str засылается строка из
файла f1. При выполнении функции fscanf() в str последовательно засылается
из файла набор символов, расположенных между пробелами.
3. Массив строк
char str[10][81];
im = j;
strcpy(sr, st[i]); // Перестановка строк.
strcpy(st[i], st[im]);
strcpy(st[im], sr); }
cout << "\n После сортировки:\n";
for (i = 0; i < k; i++)
printf("%s", st[i]);
getch(); }
Программа 10
void main()
{FILE *f1;
char ch = ' ', *str = ",. !?:;:'\n\0";
char st[81], sr[81]; // sr – для строки из файла.
// st – для слова из строки.
int k = -1, i;
clrscr();
f1 = fopen("str_w.dat", "r");
do { fgets(sr,80,f1); // Ввод строки из файла.
for (i = 0; i < strlen(sr); i++) // Начало цикла по i.
{ ch = sr[i];
if (strchr( str,ch) ) // Если символ не разделитель, то иди на
// формирование слова.
case 18432:
printf("Вы нажали клавишу «Стрелка вверх»\n");
break;
case 20480:
printf("Вы нажали клавишу «Стрелка вниз»\n");
break;
case 15104:
printf("Вы нажали клавишу F1\n");
break;
case 15360:
printf("Вы нажали клавишу F2\n");
break;
case 17408:
printf("Вы нажали клавишу F10\n");
break;
default:
printf("Клавиша с кодом %d в программе не используется \n", key);
break;
}
}
while (key != 283); // Код 283 соответствует клавише Esc.
}
План
1. Структуры.
2. Указатели на структуру.
1. Структуры
struct <имя>
{
<тип 1 > <имя 1 >;
<тип 2 > <имя 2 >;
…
<тип k > <имя k >;
};
Адреса полей:
адрес переменной sp – N1
адрес поля nam – N1
адрес поля mos – N2
адрес поля vel – N3
адрес поля vid – N4
адрес поля ves – N5
Здесь N1, N2, N3, N4, N5 – адреса полей переменной sp.
Замечание 2. Вывод адресов полей структуры дается в десятичной
системе исчисления. Легко заметить в этом случае, что поля структуры в
памяти непрерывно следуют друг за другом и адреса отличаются на число
байт, которое зарезервировано для текущего поля.
2. Указатели на структуру
План
1. Интерфейс пользователя.
2. Графический интерфейс пользователя.
3. Оконный интерфейс.
4. Текстовый режим.
5. Графический режим.
1. Интерфейс пользователя
Представляет собой совокупность средств, при помощи которых
пользователь общается с различными устройствами, чаще всего с
компьютером или бытовой техникой. Интерфейс пользователя
компьютерного приложения включает:
средства отображения информации, отображаемую информацию,
форматы и коды;
командные режимы, язык «пользователь – интерфейс»;
устройства и технологии ввода данных;
диалоги и взаимодействие между пользователем и компьютером,
обратную связь с пользователем;
поддержку принятия решений в конкретной предметной области;
порядок использования программы и документацию на нее.
3. Оконный интерфейс
Представляет собой способ организации полноэкранного интерфейса
программы, в котором каждая интегральная часть располагается в окне,
находящемся в произвольном месте над основным экраном. Несколько окон,
одновременно располагающихся на экране, могут перекрываться, находясь
выше или ниже друг относительно друга. Хотя наиболее естественным для
4. Текстовый режим
где int left, int top и int right, int bottom – координаты левого верхнего и
правого нижнего угла окна соответственно. Задание окна void windows(1,1,
80, 25); означает весь экран. Если окно не задавать, то оно всегда имеет
размер windows(1,1, 80, 25). В следующем примере в заданное окно начиная с
пятой позиции по горизонтали зелеными буквами на красном фоне
выводится текст:
#include <conio.h>
int main(void){
textbackground(0);
clrscr();
window(50,11,70,13);
textcolor(2);
textbackground(4);
gotoxy(5, 1)
cprintf("Это тест\r\n");
getch();
return 0;
}
Функция позиционирования курсора имеет прототип void gotoxy(int x, int y).
5. Графический режим
Предназначен для вывода на экран графиков, рисунков и др. В этом
режиме экран монитора представляет собой множество точек (пикселов).
Инициализация графики. В состав графического пакета входят:
заголовочный файл graphics.h,
библиотечный файл graphics.lib,
драйверы графических устройств (*.bgi),
шрифты (*.chr).
Управление экраном в графическом режиме производится с помощью
набора функций, прототипы которых находятся в заголовочном файле
graphics.h. Для работы в графическом режиме файл graphics.h должен быть
подключен с помощью директивы #include. Перед использованием
графических функций необходимо переключить видеоадаптер в графический
режим (по умолчанию он находится в текстовом режиме). Для
инициализации графического режима предназначена функция initgraph(). Ее
прототип имеет вид
где int *driver – тип подключаемого драйвера, int *mode – режим работы под-
ключенного драйвера, char *path – местоположение драйвера.
Функция initgraph() считывает в память указанный драйвер,
устанавливает видеорежим, соответствующий аргументу mode, и определяет
маршрут к директории, в которой находится файл *.bgi (драйвер). Если
маршрут не указан, то предполагается, что этот файл расположен в текущей
директории. В дальнейшем будем полагать, что драйвер находится в
текущей директории. При использовании initgraph() можно указать или
конкретный драйвер (например, egavga.bgi), или задать автоматическое
определение типа видеоадаптера и выбора соответствующего драйвера. Это
позволяет без изменения переносить программы на компьютеры с другими
видеоадаптерами. Например,
initgraph(&grdrv,&grmod,” ”);
void closegraph(void);
BLACK Черный 0
BLUE Синий 1
GREEN Зеленый 2
CYAN Циановый 3
RED Красный 4
MAGENTA Малиновый 5
BROWN Коричневый 6
LIGHTGRAY Светлый серый 7
DARKGRAY Темный серый 8
LIGHTBLUE Голубой 9
LIGHTGREEN Светлый зеленый 10
LIGHTCYAN Светлый циановый 11
LIGHTRED Светлый красный 12
LIGHTMAGENTA Светлый малиновый 13
YELLOW Желтый 14
WHITE Белый 15
где int index – номер из палитры, а int color – цвет (для палитры EGA
диапазон от 0 до 63). Настройка палитры EGA осуществляется функцией
0 SOLID_LINE сплошная
1 DOTTED_LINE пунктирная
2 CENTER_LINE штрихпунктирная
3 DASHED_LINE штриховая
4 USERBIT_LINE задается пользователем
где color – цвет текста, font – тип шрифта, direction – направление вывода
текстовой информации, charsize – множитель, влияющий на размер.
Работа с частями экрана. Довольно часто при работе с графикой
возникает ситуация, когда фрагмент изображения необходимо передвинуть в
другое место на экране. Для этих целей можно применять функции getimage()
и putimage(). Функция
void getimage(int left, int top, int right, int bottom, void *bitmap);
void far putimage(int left, int top, void far *bitmap, int op);
0 COPY_PUT копия
1 XOR_PUT исключающее «или»
2 OR_PUT «или»
3 AND_PUT «и»
4 NOT_PUT копия источника с инверсией
План
1. Функции.
2. Локальные и глобальные переменные.
3. Область действия функции.
4. Передача параметров в функцию.
5. Передача массивов в функцию.
1. Функции
Программа fun_1
void vivod()
{ cout << “Доброе утро, СТУДЕНТ !!!\n”; }
Программа fun_2
void vivod()
{
cout << “Доброе утро, СТУДЕНТ !!!\n”;
}
void main()
{ clrscr();
vivod(); // Вызов функции vivod().
getch(); }
Программа fun_3
float ymn(float x, float y)
{ float z;
z = y*x;
return z; }
Программа fun_3а
float ymn(float x, float y)
{ return x*y; }
Программа fun_4
float ymn(float a, float b); // Прототип функции.
void main()
{ int x=2, y=5;
cout << x << “*“ << y << “= “ << ymn(x,y) << “\n”;
getch();
}
void main()
{
…
// 1-й блок.
{
int x, y, z;
...
}
// 2-й блок.
{
int x, y, z;
...
}
}
Программа fun_5
float ymn(float x, float y); // Прототип функции ymn().
int w= -10;
void main()
{ cout << “До входа в 1-й блок w= “ << w << “\n”;
// 1-й блок.
{ int x = 2, y = 5, z = 3;
w = x * y + z;
}
cout << “После выхода из 1-го блока w= “ << w << “\n”;
// 2-й блок.
{ int x = 5, y = 7, z = 2;
w= (x + y) * z;
}
cout << << “После выхода из 2-го блока w = “ << w << “\n”;
cout << “После обращения к функции ymn = “ << ymn(10,10) << “\n”;
cout << “После обращения к функции w = “ << w << “\n”;
getch(); }
float ymn(float x, float y) // Функция определяет произведение 2-х чисел.
{ float z;
z = y * x + w;
w = 500;
return z; }
2.5*2= 5
Директива
r = 7*ymn(3.5,2);
Например, пусть функция имеет заголовок void f_1(int x). В этом случае
передача параметра в тело данной функции будет происходить по значению,
а именно: информация из памяти вызывающей программы пересылается
(переписывается, копируется) в память функции. Этот способ передачи
параметров схематично демонстрируется на рис. 15.1, где A – адрес области
памяти, находящейся под контролем программы, из которой функция
вызывается; B – адрес области памяти под контролем вызываемой функции.
При таком способе передачи надо иметь в виду, что вызываемая функция
не может изменить информацию в области памяти A.
A B
х x
Число x из А пересылается в B
Рис. 15.1
A B
x А Адрес А пересылается в указатель B A
Рис. 15.2
Адрес переменной y = Ny
Значение переменной y до вызова функции y = 2
Выполняется функция
В указатель x передан адрес переменной y. x = Ny
В этом адресе число = 2;
После преобразования переменной в функции x = 10;
Вышли из функции
Значение переменной y после вызова функции y = 10
void main()
{ int y = 2;
cout << “Значение переменной до вызова функции y = ” << y << “\n”;
f_1(y);
cout << “Вышли из функции.\n\n”;
cout << “Значение переменной y после вызова функции y = ” << y << “\n”;
}
void f_1(int &x)
{
cout << “Выполняется функция.\n\n”;
cout << “В функцию передано значение ”<< “x= “ << x << “\n”;
x = 10;
cout << “После преобразования переменной в функции x = “ << x << “\n”;
}
int w[n];
clrscr();
vvod(w); // Вызов функции ввода элементов массива.
vivod(w); // Вызов функции вывода элементов массива на экран.
getch();
}
void vvod(int *y) // Функция ввода элементов массива, y – указатель.
{ int i;
cout << “Введи элементы массива: \n”;
for (i = 0; i < n; i++)
{
cout << "a(" << i << ")= ";
scanf("%d", &y[i]);
}
}
Программа fun_10.
const n = 3, m=2;
void vvod(int x[][m]); // Функция ввода элементов матрицы.
void vivod (int x[][m]); // Функция вывода элементов матрицы.
void main()
{
int w[n][m];
clrscr();
vvod(w); // Вызов функции ввода элементов матрицы.
vivod(w); // Вызов функции вывода элементов матрицы на экран.
getch();
}
getch(); }
// Функция ввода элементов матрицы.
void vvod( int *c, int n, int m) // Ввод элементов матрицы с клавиатуры.
{ int i, j; // Индексы для элементов матрицы.
cout << “Введи элементы матрицы: \n”;
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
{
cout << “x(” << i << “,” << j << “) = “;
scanf(“%d”, c + i * m + j); // i * m + j – порядковый номер элемента
// матрицы c индексом i, j. x + i * m + j – адрес этого элемента.
}}
// Функция вывода элементов матрицы на экран.
void vivod( int *c, int n, int m) // Вывод элементов матрицы.
{ int i, j;
cout << “\nВведена матрица:\n”;
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
// i * m + j – порядковый номер элемента матрицы с индексом i, j.
printf("%d ", *(c + i * m + j)); // c + i * m + j – адрес этого элемента.
printf ("\n");
}}
План
1. Обработка исключений.
2. Абстрактный тип данных.
3. Инкапсуляция.
4. Классы и объекты.
1. Обработка исключений
}
void main ()
{
int t;
char st1[20];
st1[0]='\0';
cout << "\t\tПрограмма демонстрирует обработку исключений.\n\n";
try
{
vvod_n (t, st1);
cout << "Введено число k= " << t << "\n";
}
catch ( int m)
{
cout << "Зафиксирована ошибка с кодом " << m << "\n";
}
try
{
vvod_s(st1);
cout << "Введена строка:\n " << st1 << "\n";
}
catch (int m)
{
cout << "\nСтрока не введена. Ошибка с кодом " << m << "\n";
}
cout << "Конец работы программы." << "\n";
}
3. Инкапсуляция
4. Классы и объекты
classs <имя>
{
public:
<открытые функции и переменные>
};
или
classs <имя>
{
<закрытые функции и переменные>
};
return y;
}
// Функция-член mod():
// возвращает значение расстояния от начала координат до точки M( x, y).
float mod(POINT m)
{
return sqrt(m.x*m.x + m.y*m.y);
}
};
void main()
{
POINT m; // Определяем объект m типа POINT.
float x, y;
clrscr();
cout << "Введите координаты точки x y: ";
cin >> x >> y;
m.set_xy(x, y); // Инициализируем объект m.
// Выводим координаты объекта m.
cout << "\n\n\tM(" << m.x_out() << ", " << m.y_out() << ")\n";
// Выводим рассояние от начала координат до точки m.
cout << "\n\n\tR= " << m.mod(m) << "\n";
getch();
}
M(3,4)
R=5
План
1. Реализация АТД на примере комплексных чисел.
2. Конструктор класса.
3. Деструктор класса.
4. Файл реализации.
5. Файл приложения.
2. Конструктор класса
3. Деструктор класса
4. Файл реализации
5. Файл приложения
В стеке 5 элементов
543210
В стеке 0 элементов
План
1. Спецификация.
2. Параметризация.
1. Спецификация
Таблица 18.1
Возможные доступы при наследовании
2. Параметризация
Язык C допускает параметризацию абстрактного типа данных с
помощью шаблонных классов. При создании функций возникают ситуации,
План
1. Генератор кодов.
2. Пример формирования окна.
1. Генератор кодов
do
{ clrscr();
cout << "\n\n\t\tКоманды меню: \n\n";
cout << "\t1: Определяет сумму чисел a+b \n";
cout << "\t2: Определяет разность чисел a-b\n";
cout << "\t3: Определяет произведение чисел ab\n";
cout << "\t4: Определяет частное чисел a:b\n";
cout << "\t5: Выход\n";
cout << "\nВведи номер команды меню ";
cin >> k;
cout << "\n\n";
if ( k >= 1 && k <= 4)
{ cout << "Введи два числа a b = ";
cin >> a >> b;
cout << "\n\t\t";
}
switch (k)
{
case 1: c = a+b;
cout << a << " + " << b << " = " << c << "\n";
break;
} getch();
} while (1); }
Команды меню:
1: Определяет сумму чисел a+b
2: Определяет разность чисел a+b
3: Определяет произведение чисел ab
4: Определяет частное чисел a+b
5: Выход
Введи команды меню 2 <Enter>
Введи два числа a b = 10.2 13 <Enter>
10-13 = -2.8
План
void rec(...)
{
rec(...);
}
Программа 1
#include <conio.h>
#include <iostream.h>
void separ( long n);
void main()
{
long a;
clrscr();
cout << "Введи целое число a= " ;
cin >> a;
cout << "Число " << a << " состоит из цифр: \n";
separ(a);
getch();
}
Программа 2
#include <conio.h>
#include <iostream.h>
void separ( long n);
int ms[12];
void main()
{
long a;
clrscr();
cout << "Введи целое число a= " ;
cin >> a;
cout << "Число " << a << " состоит из цифр: \n";
separ(a);
getch();
}
void separ( long a)
{
int j = -1, i;
while ( a != 0 )
{
j++;
ms[j] = a % 10;
a = a/10;
}
for ( i = j; i >= 0; i--)
cout << ms[i] << "\n";
}
Программа 3
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
void separ( long n );
void main ()
{ long n;
clrscr();
cout << "Введи число n= " ;
cin >> n;
separ(n);
getch(); }
Рекурсивная функция:
void separ( long n )
{ long a;
if ( n < 10 )
cout << n << "\n";
else
{
a = n / 10;
separ(a);
//AV
cout << n%10 << "\n";
}}
или
void rec()
{
if ( <условие>)
{
s1
}
else
{
s2;
rec();
}
}
void rec()
{
if ( <условие>)
{
s1;
}
else
{
rec();
s2;
}
}
или
void rec()
{
if ( <условие>)
{
s;
}
else
{
s1;
rec();
s2;
}
}
Программа 4
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
int n, f=1;
void fact( int l );
void main ()
{
clrscr();
cout << "Введи число n= " ;
cin >> n;
fact (1);
getch();
}
void fact( int k )
{
if ( k <= n )
{
f = f * k;
cout << "На шаге k= " << k << " значение f*k = " << f << "\n";
fact ( k+1);
}
}
Для того чтобы провести анализ программы 4, приведем еще код
вычисления n! в нерекурсивном (итерационном) варианте.
Программа 5
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
int n, f=1;
void fact( int l );
void main ()
<оператор цикла>
{
<Тело цикла>;
}
void main(void)
{
const n = 10;
int i, a[n];
clrscr();
f1 = fopen("tr.dat", "r");
vvod_1(a,0,n);
fclose(f1);
f1 = fopen("tw.dat", "w");
vivd_1(a,0,n);
fclose(f1);
}
// Рекурсивная функция ввода элементов массива из файла.
void vvod_1(int a[], int i, int n)
{
if ( !feof(f1) )
{
fscanf( f1, "%d", &a[i] );
vvod_1(a,i+1,n);
}
}
// Рекурсивная функция записи элементов массива в файл.
Рекурсивный вариант:
void vivod( int k, int n )
{
if ( k <= n )
{
printf("Это – %d\n",k);
vivod(k+1, n);
}
x1 x 2 x3 xn
Программа 9. Нахождение суммы ряда S = + + + ... + .
1! 2! 3! n!
Итерационный вариант:
void f_exp(int i, int n)
{
for ( i = 1; i <= n; i++ )
{
f = f * i;
xs = xs * x;
s = s + xs / f;
cout << "Значение суммы на " << i << "-м шаге = " << s << "\n";
}
}
Рекурсивный вариант:
void f_exp(int i, int n)
{ if ( i <= n )
{
f = f * i;
xs = xs * x;
План
1. Выполнение действий на рекурсивном возврате.
2. Выполнение действий на рекурсивном спуске и возврате.
// Рекурсивная функция
void fact( int k )
{ if ( k >=1 )
{
fact ( k-1);
c = getch();
putch(c);
if (c != 13)
{
rev_str();
putch(c);
}
else cout <<"\n";
}
void main()
{ clrscr();
cout << "Вводи строку:\n";
rev_str();
getch(); }
r = fact(n-1);
r = n*r;
return r;
План
1. Быстрая сортировка с использованием рекурсивных функций.
2. Быстрая сортировка с использованием циклов.
Рис. 22.1
{
int ir, rm, buf; // ir – для индекса среднего элемента
// текущей части массива.
// rm – для значения среднего
// элемента текущей части массива.
// buf – для промежуточных значений
// обрабатываемой информации.
int l1, r1; // l1 – для индекса элемента, который
// расположен левее среднего и
// больше среднего элемента.
// r1 – для индекса элемента, который
// расположен правее и
// меньше среднего элемента.
int i; // i – для управляющего параметра цикла.
l1 = lef; // Определяем начальный индекс текущей части
// массива.
r1 = rit; // Определяем конечный индекс текущей части
// массива.
ir = (l1+r1)/2; // Определяем индекс среднего элемента
// текущей части массива.
rm = a[ir]; // В rm пересылаем значение среднего
// элемента текущей части массива.
do // Цикл для перебора элементов, которые
// находятся левее и правее среднего
// элемента текущей части массива.
{
}
while ( r1 > l1 );
if ( lef < r1 ) qsort(a, lef, r1);
//AV
if ( l1 < rit ) qsort(a, l1, rit);
//AV1
}
определены значения двух пар локальных переменных lef, r1 и l1, rit. Если
оказывается, что неравенство lef < r1 справедливо, то прерывается текущее
обращение к функции и выполняется новое рекурсивное обращение
qsort(a, lef, r1). В результате этого нового рекурсивного обращения в память
стека размещается набор локальных параметров и адрес (//AV), с которого
в определенный момент возобновится прерванное текущее обращение.
Аналогично: если справедливо неравенство l1 < rit, то выполняется
рекурсивное обращение qsort(a, l1, rit) и в память стека размещается набор
локальных параметров и адрес (//AV1), с которого в определенный момент
возобновится прерванное текущее обращение. Рекурсивные возвраты
происходят тогда, когда не происходит рекурсивного вызова. Заметим, что
прерванные обращения начинаются либо с директивы, которая следует после
комментария //AV, либо с директивы, которая следует за комментарием //AV1.
Но после комментария //AV1 выполнение функции завершается. Поэтому
если рекурсивный возврат начинает выполняться после комментария //AV1,
то тут же происходит следующий рекурсивный возврат. Теперь перейдем к
более детальной трассировке работы программы qsort().
Итак, при вызове qsort() из головной программы в локальные
переменные lef и rit соответственно передадутся числа 0 и 8. После входа
в функцию программа выполняет основной шаг 2 алгоритма быстрой
сортировки, а именно: массив разбивается на два массива – левый и правый –
относительно среднего элемента. В левом располагаются элементы меньше
среднего, в правом – больше среднего.
Первый и последний индексы первой части массива засылаются
соответственно в переменные lef и r1, а в ll и rit засылаются соответственно
первый и последний индексы второй части массива. Перед выполнением
директивы if (lef < r1) qsort(a, lef, r1) в перечисленных выше переменных
будут определены следующие значения: lef = 0, r1 = 3, ll = 5, rit = 8.
Поэтому при первом рекурсивном обращении функции к самой себе
состояние памяти стека будет следующим.
Программа 4
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
void qsort (int a[], int lef, int rit);
FILE *f1;
const n = 20;
void main()
{
int i, a[n];
clrscr();
f1 = fopen("speed_w.dat", "r");
for (i = 0; i <= n-1; i++)
fscanf( f1, "%d", &a[i] );
fclose(f1);
qsort (a,0,n-1); // Обращение к процедуре быстрой сортировки.
for (i = 0; i <= n-1; i++ )
printf("%d ", a[i]);
getch();
}
do {Тело цикла}
while (st >= 0);
План
1. Односвязный список.
2. Формирование списка.
3. Операции над списком.
4. Программа обработки списка.
1. Односвязный список
struct node
{
int inf; // Поле для записи целых чисел.
node *next; // Поле указателя для записи адресов элементов типа node.
};
2. Формирование списка
Fr NULL
er
Рис. 23.1
r A1
A1 inf next
Fr NULL
er
Рис. 23.2
r A1
A1 inf next
Fr NULL a1
er
Рис. 23.3
A1 inf next
Fr NULL a1 NULL
er
Рис. 23.4
r A1
A1 inf next
Fr A1 a1 NULL
er
Рис. 23.5
r A1
A1
Fr A1 a1 NULL
er A1
Рис. 23.6
r A2
A1 A2
Fr A1 a1 NULL
er A1
Рис. 23.7
r A2
A1 A2
Fr A1 a1 NULL a2
er A1
Рис. 23.8
r A2
A1 A2
Fr A1 a1 NULL a2 NULL
er A1
Рис. 23.9
r A2
A1 A2
Fr A1 a1 A2 a2 NULL
er A1
Рис. 23.10
r A2
A1 A2
Fr A1 a1 A2 a2 NULL
er A2
Рис. 23.11
er -> next = r.
r A3
A1 A2 A3
Fr A1 a1 A2 a2 NULL
er A2
Рис. 23.12
A1 A2 A3
Fr A1 a1 A2 a2 NULL a3
er A2
Рис. 23.13
r A3
A1 A2 A3
Fr A1 a1 A2 a2 NULL a3 NULL
er A2
Рис. 23.14
r A3
A1 A2 A3
Fr A1 a1 A2 a2 A3 a3 NULL
er A2
Рис. 23.15
r A3
A1 A2 A3
Fr A1 a1 A2 a2 A3 a3 NULL
er A3
Рис. 23.16
r An
Fr A1
A1
a1 A2
A2
a2 A3 ... An an NULL
er An
Рис. 23.17
В заключение заметим, что адреса A1, A2, A3, ..., An, которые
использовались в схемах, в реальных задачах принимают конкретные
значения. Более того, при желании значения этих адресов можно вывести на
экран, например, используя код
cout << r -> inf << "\n"; – вывод на экран поля inf элемента списка,
адрес которого записан в указателе r;
r = r -> next; – в указатель r засылается адрес следующего
элемента списка.
A1 A2
a1 A2 a2 A3
Рис. 23.18
В этом состоянии на экран выводится поле inf (т. е. число a1) элемента
списка, адрес которого содержится в r. На рис. 23.19 приведен фрагмент
списка после выполнения директивы r = r -> next.
r A2
A1 A2
a1 A2 a2 A3
Рис. 23.19
while (r != NULL)
{ if (r-> inf == a) break;
r = r -> next;
}
можно заменить на следующий:
while (r != NULL && r -> inf != a)
r = r -> next;
Удаление элемента из списка. Решение данной задачи зависит от
расположения элемента в списке. Способ удаления головного (первого)
элемента списка отличается от способа удаления любого другого элемента.
Рассмотрим фрагмент списка, представленный на рис. 23.20.
A1 A2
Fr A1 a1 A2 a2 A3
Рис. 23.20
r = fr;
fr = fr -> next;
delete r;
A1 A2
Fr A2 a1 A2 a2 A3
Рис. 23.21
r A1
A2
Fr A2 a2 A3
Рис. 23.22
r Ak
A[k-1] Ak A[k+1]
a[k-1] Ak ak A[k+1] a[k+1] A[k+2]
rp A[k-1]
Рис. 23.23
r Ak
A[k-1] Ak A[k+1]
a[k-1] A[k+1] ak A[k+1] a[k+1] A[k+2]
rp A[k-1]
Рис. 23.24
r Ak
A[k-1] A[k+1]
a[k-1] A[k+1] a[k+1] A[k+2]
rp A[k-1]
Рис. 23.25
void main()
{
node *r, *fr = NULL, *er; // fr – указатель на головной элемент списка.
// er – указатель на последний элемент списка.
// r – указатель для формирования нового узла списка.
node *rp;
int a; // a – переменная для записи целых чисел.
clrscr();
FILE *f;
f = fopen("t.dat","r");
}
while (!feof(f)); // Конец цикла ввода чисел из файла.
fclose(f);
// Конец формирования списка.
// Вывод списка на экран.
cout << "Сформирован список:\n";
r = fr;
while (r != NULL) // Пока не дошли до последнего элемента списка.
{ cout << r -> inf << "\n"; // Вывод информации из поля inf элемента,
// адрес которого находится в указателе r.
r = r -> next; // Переход к следующему элементу списка.
// Для этого из поля next текущего элемента списка
// в указатель r пересылаем адрес на следующий элемент.
}
r Ak
Ak A[k+1]
a[k-1] A[k+1] a[k+1] A[k+2]
Рис. 23.26
r Ak
Ak A[k+1]
a[k-1] A[k+1] a[k+1] A[k+2]
AN
rp AN
Рис. 23.27
r Ak
Ak A[k+1]
a[k-1] A[k+1] a[k+1] A[k+2]
AN
b A[k+1]
rp AN
Рис. 23.28
AN
b A[k+1]
rp AN
Рис. 23.29
AA1
1 AA2
2 AA3
3
Fr
Fr
A1
A1 aa1
1 AA22 aa22 AA3
3
Рис. 23.30
AN
AN
rp
rp AN
AN b AA1
1
Fr
Fr AA1
1 AA2
2 A
A33
A1 aa11 A2 aa22 AA3
3
A2
Рис. 23.31
AN
AN
rp
rp AN
AN bb AA11
Fr AA11 AA22 A
A33
Fr AN
AN aa1
1 AA22 aa22 AA33
Рис. 23.32
struct node
{
int inf;
node *next;
};
void viv_sps ( node *r);
node *add_1(int k);
void vstv( node *r, int b);
node *poisk (node *r, int b);
void main()
{
node *fr, *r, *p_r; // fr – указатель для головного элемента.
int k, l, h;
fr = NULL;
clrscr();
int iv = FALSE;
while ( !iv )
{
clrscr();
cout << "\n\nКоманды меню:\n\n";
cout << "1 Сформировать список\n";
cout << "2 Вставить в список\n";
cout << "3 Вставить в список перед головным элементом\n";
cout << "4 Удалить из списка\n";
cout << "5 Выдать на экран\n";
cout << "6 Выход\n";
cout << "\nВведи команды меню: ";
scanf("%d",&k);
switch (k)
{
case 1 : // Формируется список.
cout << "Введи узел (Ctrl + z Enter – конец ввода): ";
scanf("%d", &h);
do
{
if (fr == NULL)
{
fr = p_r = add_1(h);
}
else
{
p_r -> next = add_1(h);
p_r = p_r -> next;
}
scanf("%d", &h);
}while (!feof(stdin));
break;
План
1. Стек.
2. Операции над стеком.
3. Программа обработки стека.
1. Стек
void push (node **h, int ad); // Функция добавляет элемент в стек.
void pop (node **h, int *ad); // Функция удаляет элемент из стека.
void spisprint(node *p); // Функция выводит стек на экран.
void main(void)
{
node *rp=NULL, *wh;
int no = 0; // Для текущего элемента в стеке.
int iv = FALSE; // iv – параметр, который управляет работой меню.
int k;
rp = NULL;
while ( !iv )
{
clrscr();
cout << "\n\nКоманды меню:\n\n";
cout << "1 Добавить в стек\n";
cout << "2 Удалить из стека\n";
cout << "3 Выдать на экран\n";
cout << "4 Выход\n";
cout << "\nВведи команды меню: ";
scanf("%d",&k);
switch (k)
{ case 1 : // Добавить элемент в стек.
cout << "Введи ключ (значение) элемента – ";
cin >> no;
push(&rp, no );
break;
{
node *r;
if ( *h == NULL )
void add_o (node **h, node **sl, int ad); // Функция добавляет элемент
// в очередь.
void del_o (node **h, int *ad); // Функция удаляет элемент из очереди.
void spisprint(node *p); // Функция выводит очередь на экран.
void main(void)
case 2 :
del_o(&fo, &k ); // Удалить элемент из очереди.
if ( k > 0 )
cout << "Удалили элемент " << k << " Нажми Enter\n";
getch();
break;
План
1. Двусвязные списки.
2. Формирование списка.
3. Операции над списком.
4. Программа обработки списка.
1. Двусвязные списки
...
Рис. 25.1
lt inf rt
Рис. 25.2
2. Формирование списка
A1 A1
NULL NZ NULL
lt inf rt lt inf rt
fr r A1 er fr A1 r A1 er A1
AA1
1 NULL
NULL aa1
1 A2
A2 ... A(N–1)
A (N-1) aN
aN NULL
NULL
l l tt inf
inf rtrt ll t inf
inf rtrt
ff r A 1
A1 ee nn AN
AN
Рис. 25.5
AA1
1 NULL
NULL aa1
1 A2
A2 ... ANAN
A(N–1)
A (N-1) aN
aN NULL
NULL
AK
AK
llt inf
inf rtrt l ltt inf
inf rtrt ll t inf
inf rtrt
ff rr A
A11 ee n AN
AN r AK
Рис. 25.6
A1
NULL a1 A2 ... AN
A (N-1) aN NULL
AK
nz
Рис. 25.7
AA1
1 NULL
NULL aa1
1 A2
A2 ... AN
AN
A(N–1)
A (N-1) aN NULL
NULL
AK
AK
nz
nz NULL
NULL
l l tt inf
inf rtrt ll t inf
inf rtrt l ltt inf
inf rtrt
ff r A1
A1 ee n AN
AN r AKAK
Рис. 25.8
AA1
1 NULL
NULL aa1
1 A2
A2 ... AN
AN
A(N–1)
A (N-1) aN
aN NULL
NULL
AK
AK
AN
AN nz
nz NULL
NULL
l ltt inf
inf rtrt ll t inf
inf rtrt l l tt inf
inf rtrt
f rr A1
A1 ee nn AN
AN rr AK
Рис. 25.9
AA1
1 NULL
NULL aa1
1 A2
A2 ... AN A(N–1)
A (N-1) aN AK
AK
AK
AK
AN
AN nz
nz NULL
NULL
lt inf
inf rtrt l tt inf
inf rtrt ll t inf
inf rtrt
A1
ff r A1 ee nn ANAN rr AK
Рис. 25.10
AA1
1 NULL
NULL aa1
1 AA2
2 ... AN
AN
A(N–1)
A (N-1) aN
aN AK
AK
AK
AK
AN
AN nz
nz NULL
NULL
ll t inf
inf rtrt ll t inf
inf rtrt l l tt inf
inf rtrt
ff r AA1
1 ee nn AKAK rr AK
Рис. 25.11
AXL AX AXR
AXL AP
AP aa AX
AX AXR
AXL bb AXR
AXR AX
AX c AS
AS
ll t inf
inf rtrt ll t inf
inf rtrt ll t inf
inf rtrt
rr AX AX
Рис. 25.12
AXL AP AX
AX AXR
AXR
AXL AP aa AXR c AS
AXL
AXL bb AXR
AXR AXL
AXL AS
ll t inf
inf rtrt ll t inf
inf rtrt l l tt inf
inf rtrt
rr AX
Рис. 25.13
if (r == en)
{ en = r -> lt; // В указатель для адреса последнего узла
AV AN AVR
AVR
AVL
AVL a AVR
AVR AV
AV cc AS
AS
ll t inf
inf rt
rt l l tt inf
inf rtrt ll t inf
inf rt
rr AV vv AN
Рис. 25.14
AN
AV AN AVR
AVR
AVL
AVL a
a AVR
AVR nnv
v AV
AV cc AS
AS
ll t inf
inf rt ll tt inf
inf rtrt ll t inf
inf rt
rt
rr AV vv AN
Рис. 25.15
AV AN
AN AVR
AVR
AVL
AVL a
a AVR
AVR nnv
v AVR
AVR AV
AV cc AS
AS
ll t inf
inf rtrt ll tt inf
inf rt
rt ll t inf
inf rt
rt
rr AVAV vv AN
AN
Рис. 25.16
AV
AV AN AVR
AVR
AVL
AVL a AVR
AVR AAV
V nnv
v AVR
AVR AV
AV cc AS
AS
ll t inf
inf rtrt ll tt inf
inf rt
rt ll t inf
inf rt
rr AVAV vv AN
Рис. 25.17
AV
AV AN
AN AVR
AVR
AVL
AVL aa AVR
AVR AV
A V nnv
v AVR
AVR AV
AN cc AS
AS
ll tt inf
inf rtrt ll tt inf
inf rt
rt l l tt inf
inf rt
rt
rr AV vv AN
Рис. 25.18
AV
AV AN
AN AVR
AVR
AVL
AVL aa AN
AN AV
A V nnv
v AVR
AVR AN
AV cc AS
AS
l ltt inf
inf rtrt ll tt inf
inf rt
rt l l tt inf
inf rtrt
rr AV AV vv AN AN
Рис. 25.19
План
47
A 21 77
14 43 65 93
7 16 31 44 68
B C
15
struct <имя_1>
{
<тип> <имя>;
<имя_1> * <left>;
<имя_1> * <rite>;
}
struct node
{
int key;
node *l, *r; };
int n;
FILE *f;
node *tree(node *p, int h)
{ if ( p == NULL )
{ p = new node;
p -> key=h;
p -> l = NULL;
p -> r = NULL;
}
return p;
}
void main()
{
node *root, w;
int h;
clrscr();
root = &w; // В указатель root засылаем адрес
// переменной w.
// Вывод значения переменной root до обращения к функции.
{
root = tree(root,h);
// Вывод значения переменной root после обращения к функции.
cout << "При h= " << h << " root= " << root << "\n";
}
getch(); }
root h
NULL 14
Рис. 26.3
root h p h
NULL 14 NULL 14
Рис. 26.4
p
h root A0
NULL A0
14
l r
h
14
Рис. 26.5
h root p h
14 NULL A0 14
A0 14
NULL NULL
l r
Рис. 26.6
h root
14 A0
A0 14
NULL NULL
l r
Рис. 26.7
h root p h
10 A0 A0 10
A0 14
NULL NULL
p->l p->r
Рис. 26.8
h root p h
10 A0 A0 10
A0 14
NULL NULL
p->l p->r
Рис. 26.9
A0 14
NULL NULL
p->l p->r
p h
NULL 10
Рис. 26.10
h root p h
10 A0 A0 10
A0 14
NULL NULL
p->l p->r
p h
NULL 10
A1 10
NULL NULL
p->l p->r
Рис. 26.11
h root p h
10 A0 A0 10
A0 14
A1 NULL
p->l p->r
A1 10
NULL NULL
l r
Рис. 26.12
A0 14
A1 NULL
l r
A1 10
NULL NULL
l r
Рис. 26.13
A0
A0
A1 A2
A1 A2
Рис. 26.14
A0
A0
A1 A2
A1 A2
A3
NULL NULL
Рис. 26.15
p -> l = NULL;
while( !feof(f) )
{
fscanf(f,"%d",&h);
root = tree(root,h);
}
fclose(f); }
План
1. Обходы дерева.
2. Идеально сбалансированное дерево.
3. Удаление узла из дерева.
1. Обходы дерева
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->key
1 1 AV1 A47 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->key
2 2 AV1 A21 A14 A43 21
1 1 AV1 A47 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->ke
3 3 AV1 A14 A7 A16 14
2 2 AV1 A21 A14 A43 21
1 1 AV1 A47 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->key
4 3 AV2 A14 A7 A16 14
2 2 AV1 A21 A14 A43 21
1 1 AV1 A47 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t ->key
5 4 AV1 A16 A15 NULL 16
4 3 AV2 A14 A7 A16 14
2 2 AV1 A21 A14 A43 21
1 1 AV1 A47 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->key
6 2 AV2 A21 A14 A43 21
1 1 AV1 A47 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->key
7 2 AV1 A43 A31 A44 43
6 2 AV2 A21 A14 A43 21
1 1 AV1 A47 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->key
8 2 AV2 A43 A31 A44 43
6 2 AV2 A21 A14 A43 21
1 1 AV1 A4 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->key
9 1 AV2 A47 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->key
10 2 AV1 A77 A65 A93 77
9 1 AV2 A47 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->key
11 3 AV2 A65 NULL A68 65
10 2 AV1 A77 A65 A93 77
9 1 AV2 A47 A21 A77 47
Состояние стека
№ п/п Глубина Возврат t t -> l t->r t->key
13 2 AV2 A77 A65 A93 77
9 1 AV2 A47 A21 A77 47
7 15 16 14 31 44 43 21 68 65 93 77 47
void rec()
{
S1;
if ( <условие> ) rec();
S2
}
void rec()
{
if ( <условие> ) rec();
if ( <условие> ) rec();
S2;
}.
struct node
{
int kl;
node *l, *r;
};
node *tree( int x );
void treeprint(node *p, int n);
FILE *f;
void main()
{
node *root;
int n;
clrscr();
f = fopen("bal.dat","r");
fscanf(f,"%d",&n); // Считываем из файла количество узлов.
root = tree(n);
fclose(f);
treeprint( root, 0);
getch();
}
}
else
{
nl = n / 2; // Определяем число узлов в левом поддереве.
nr = n - nl -1; // Определяем число узлов в правом поддереве.
fscanf(f,"%d",&x); // Ввод нового ключа узла из файла.
}
}
Z C Z C
D D W
E F E N
Рис. 27.1
Рис. 27.2
A a1 A a1 A a1
Z z1 W w1 Z y1 W w1 Z y1 W w1
C c1 B b1 C c1 B b1 C c1 B b1
D d1 Y y1 D d1 Y y1 D d1
E e1 E e1 E e1
Рис. 27.3
План
1. Классы сортировок.
2. Сортировка выбором.
3. Сортировка обменом (методом пузырька).
4. Сортировка вставками.
5. Пирамидальная сортировка.
1. Классы сортировок
2. Сортировка выбором
Шаг 1: 12 24 -36 7 25 0
Шаг 2: -36 24 12 7 25 0
Шаг 3: -36 0 12 7 25 24
Шаг 4: -36 0 7 12 25 24
Шаг 5: -36 0 7 12 25 24
Шаг 6: -36 0 7 12 24 25
Рис. 28.1
Шаг 1: 12 24 -36 7 25 0
Шаг 2: 12 24 -36 7 0 25
Шаг 3: 12 24 -36 0 7 25
Шаг 4: 12 24 -36 0 7 25
Шаг 5: 12 -36 24 0 7 25
Шаг 6: -36 12 24 0 7 25
Рис. 28.2
#include <conio.h>
#include <iostream.h>
#include <stdio.h>
void main()
{
const n = 10; // n – для размера массива.
int i, a[n], x, j;
FILE *f1;
clrscr();
f1 = fopen("fr.dat","r");
cout << " Из файла введен массив: \n";
for (i = 0; i < n; i++)
{
fscanf(f1,"%d", &a[i]); // Ввод массива из файла.
printf( "%d ",a[i]);
}
#include <conio.h>
#include <iostream.h>
#include <stdio.h>
void main()
{
const n = 10; // n – для размера массива.
int i, a[n], x, j;
FILE *f1;
clrscr();
f1 = fopen("fr.dat","r");
cout << " Из файла введен массив:\n";
for (i = 0; i < n; i++)