
- •Содержание
- •1. Основные понятия и определения
- •1.1. Структура персональной ЭВМ
- •1.2. Размещение данных и программ в памяти ПЭВМ
- •1.3. Программные модули
- •1.4. Ошибки
- •1.5. Функциональная и модульная декомпозиции
- •1.6. Файловая система хранения информации
- •1.7. Операционная система
- •2. Понятие алгоритмов и способы их описания
- •2.1. Свойства алгоритмов
- •2.2. Способы описания алгоритмов
- •3. Базовые элементы языка Cи
- •3.1. Алфавит языка
- •3.2. Лексемы
- •3.3. Идентификаторы и ключевые слова
- •3.4. Знаки операций
- •3.5. Литералы (константы)
- •3.6. Комментарии
- •4. Базовые типы объектов
- •4.1. Простейшая программа
- •4.2. Основные типы данных
- •4.3. Декларация объектов
- •4.4. Данные целого типа (int)
- •4.5. Данные символьного типа (char)
- •4.6. Данные вещественного типа (float, double)
- •5. Константы в программах
- •5.1. Целочисленные константы
- •5.2. Константы вещественного типа
- •5.3. Символьные константы
- •5.4. Строковые константы
- •6. Обзор операций
- •6.1. Операции, выражения
- •6.2. Арифметические операции
- •6.3. Операции присваивания
- •6.4. Сокращенная запись операции присваивания
- •6.5. Преобразование типов операндов арифметических операций
- •6.6. Операция приведения типа
- •6.7. Операции сравнения
- •6.8. Логические операции
- •6.9. Побитовые логические операции, операции над битами
- •6.10. Операция «,» (запятая)
- •7. Обзор базовых инструкций языка Си
- •7.1. Стандартная библиотека языка Си
- •7.2. Стандартные математические функции
- •7.3. Функции вывода данных
- •7.4. Функции ввода информации
- •7.5. Ввод - вывод потоками
- •8. Синтаксис операторов языка Cи
- •8.1. Условные операторы
- •8.2. Условная операция «? :»
- •8.3. Оператор выбора альтернатив (переключатель)
- •9. Составление циклических алгоритмов
- •9.1. Понятие цикла
- •9.2. Оператор с предусловием while
- •9.3. Оператор цикла с постусловием do – while
- •9.4. Оператор цикла с предусловием и коррекцией for
- •10. Операторы передачи управления
- •10.1. Оператор безусловного перехода goto
- •10.2. Оператор continue
- •10.3. Оператор break
- •10.4. Оператор return
- •11.1. Операции над указателями (косвенная адресация)
- •11.2. Ссылка
- •12. Массивы
- •12.1. Одномерные массивы
- •12.2. Многомерные массивы
- •12.3. Операция sizeof
- •12.4. Применение указателей
- •12.5. Указатели на указатели
- •13. Работа с динамической памятью
- •13.1. Пример создания одномерного динамического массива
- •13.2. Пример создания двухмерного динамического массива
- •14. Строки в языке Си
- •14.1. Русификация под Visual
- •15. Функции пользователя
- •15.1. Декларация функции
- •15.2. Вызов функции
- •15.3. Операция typedef
- •15.4. Указатели на функции
- •16. Классы памяти и области действия объектов
- •16.1. Автоматические переменные
- •16.2. Внешние переменные
- •16.3. Область действия переменных
- •17. Структуры, объединения, перечисления
- •17.1. Структуры
- •17.2. Декларация структурного типа данных
- •17.3. Создание структурных переменных
- •17.4. Вложенные структуры
- •17.5. Массивы структур
- •17.6. Размещение структурных переменных в памяти
- •17.7. Объединения
- •17.8. Перечисления
- •18. Файлы в языке Си
- •18.1. Открытие файла
- •18.2. Закрытие файла
- •18.3. Запись – чтение информации
- •18.4. Текстовые файлы
- •18.5. Бинарные файлы
- •Литература
- •Приложение 1. Таблицы символов ASCII
- •Приложение 2. Операции языка Си
- •Приложение 3. Возможности препроцессора
- •1. Вид рабочего стола консольного приложения Visual C++
- •2. Создание нового проекта
- •3. Добавление к проекту файлов с исходным кодом
- •4. Компиляция, компоновка и выполнение проекта
- •5. Конфигурация проекта
- •6. Открытие существующего проекта
- •Приложение 5. Некоторые возможности отладчика Visual C++
- •1. Установка точки прерывания
- •2. Пошаговое выполнение программы
- •3. Проверка значений переменных во время выполнения программы
- •4. Окна Auto и Watch 1
- •5. Программные средства отладки
- •ЧАСТЬ 2
- •1. Системы счисления
- •1.1. Общие определения
- •1.2. Алгоритмы перевода из одной системы счисления в другую
- •2. Организация памяти и структуры данных
- •3. Линейные списки и рекурсия
- •4. Динамическая структура данных – СТЕК
- •5. Динамическая структура данных – ОЧЕРЕДЬ
- •6. Двунаправленный линейный список
- •7. Построение обратной польской записи
- •8. Нелинейные структуры данных
- •Пояснение к программе
- •9. Понятие хеширования
- •9.1. Хеш-функция и хеш-таблица
- •9.2. Схемы хеширования
- •10. Элементы теории погрешностей
- •10.1. Методы реализации математических моделей
- •10.2. Источники погрешностей
- •10.3. Приближенные числа и оценка их погрешностей
- •10.4. Прямая задача теории погрешностей
- •10.5. Обратная задача теории погрешностей
- •10.6. Понятия устойчивости, корректности и сходимости
- •11. Вычисление интегралов
- •11.1. Формулы численного интегрирования
- •11.2. Формула средних
- •11.3. Формула трапеций
- •11.4. Формула Симпсона
- •11.5. Схема с автоматическим выбором шага по заданной точности
- •12. Методы решения нелинейных уравнений
- •12.1. Итерационные методы уточнения корней
- •12.2. Метод Ньютона
- •12.3. Метод секущих
- •12.4. Метод Вегстейна
- •12.5. Метод парабол
- •12.6. Метод деления отрезка пополам
- •13. Решение систем линейных алгебраических уравнений
- •13.1. Прямые методы решения СЛАУ
- •13.2. Метод Гаусса
- •13.3. Метод прогонки
- •13.4. Метод квадратного корня
- •13.5. Итерационные методы решения СЛАУ
- •13.6. Метод простой итерации
- •14. Поиск и сортировка
- •14.1. Последовательный поиск в массиве
- •14.2. Барьерный последовательный поиск
- •14.3. Сортировка вставками (включениями)
- •14.4. Двоичный поиск
- •14.5. Сортировка выбором
- •14.6. Алгоритмы сортировки выбором
- •14.7. Сортировка обменом
- •14.8. Быстрая сортировка
- •15. Обработка статистических данных

2)формирование информационной части info;
3)если i = 0, то освобождаем захваченную память для созданного элемента (delete t;) и возвращаем NULL, т.е. формирование очереди закончилось.
4)иначе, т.е. i ≠ 0, формируем информационную часть и, используя адресную часть только что созданного элемента, опять обращаемся к функции input:
t -> Next = input();
и возвращаем указатель на только что созданный (последний) элемент, который будет размещен в адресной части теперь предпоследнего элемента.
4. Динамическая структура данных – СТЕК
Рассмотрим алгоритмы работы со списковыми динамическими данными на примере структуры под названием «СТЕК».
Стек – упорядоченный набор данных, в котором размещение новых элементов и удаление существующих производится только с одного его конца, который называют вершиной стека, т.е. стек это список с одной точкой доступа к его элементам. Графически это можно изобразить так:
Вход
|
3 |
2 |
1 |
|
|
|
|
Выход «Вершина стека»
Стек – структура типа LIFO (Last In, First Out) – последним вошел, первым вышел: последний добавленный элемент, который первым выйдет. Стек получил свое название из за схожести с оружейным магазином с патронами (обойма) – когда в стек добавляется новый элемент, то прежний проталкивается вниз и временно становится недоступным. Когда верхний элемент удаляется из стека, последующий за ним поднимается вверх и становится опять доступным.
Максимальное число элементов стека не должно ограничиваться, т.е. по мере вталкивания в стек новых элементов, память под него должна динамически запрашиваться и освобождаться также динамически при удалении элемента из стека. Таким образом, стек – динамическая структура данных, состоящая из переменного числа элементов одинакового типа.
Состояние стека рассматривается только по отношению к его вершине, а не по отношению к количеству его элементов, т.е. только вершина стека характеризует его состояние.
Операции, выполняемые над стеком, имеют специальные названия: push – добавление элемента в стек (вталкивание);
pop – выталкивание элемента из стека, верхний элемент стека удаляется (не может применяться к пустому стеку).
Кроме этих обязательных операций часто нужно прочитать значение элемента в вершине стека, не извлекая его оттуда. Такая операция получила название peek.
113

Стек можно реализовать или на базе динамических операций с ОП или на базе массивов. Рассмотрим реализацию списковой структуры стек на базе динамической памяти. Информационная часть элементов стека будет содержать целые числа. Тип int взят только для простоты рассмотрения, хотя информационная часть может состоять из любого количества объектов любого допустимого типа за исключением файлов.
Ниже приведены основные словесные алгоритмы для работы со стеком.
Алгоритм формирования первых 2-х элементов
1. Описать структуру переменной (рекомендуется шаблон структуры описывать глобально):
struct Stack → |
info |
Next |
|
|
|
struct Stack {
int info; Stack *Next;
};
2.Объявить два указателя на структуру:
Stack *begin (вершина стека), *t (текущий элемент);
3. Стек пуст, поэтому вершина в NULL: begin = NULL; (такое значение необходимо для первого вталкиваемого в стек элемента); графически это можно изобразить так:
begin → NULL
4. Захват памяти под первый (текущий) элемент: t = new Stack;
формируется конкретный адрес (А1) для первого элемента, т.е. t равен А1. 5. Считываем информацию (i1);
а) формируем информационную часть: t -> info = i1;
t → |
info = i1 |
Next |
|
|
|
б) значение вершины – в адресную часть (там был NULL) t -> Next = begin;
6. Вершину стека переносим на созданный (1-ый) элемент: begin = t;
получили:
begin (=A1) → info =i1 NULL
7. Опять захватываем память (под второй элемент): t = new Stack;
имеем конкретный адрес (A2) для второго элемента, т.е. по шагам 8. Считываем для него информацию (i2);
а) формируем информационную часть: t -> info = i2;
114

б) в адресную часть – значение вершины, т.е. адрес предыдущего (первого) элемента (А1):
t -> Next = begin;
t (=A2) → info = i2 Next = A1
9. Вершину стека снимаем с первого и устанавливаем на новый элемент
(A2):
begin = t;
получим:
begin (=A2) → |
Info=i2 |
Next=A1 |
|
Info=i1 |
Next=NULL |
|
|
|
|
|
|
Алгоритм добавления текущего элемента
1.Захват памяти под текущий элемент (формирование конкретного адреса).
2.Формирование информационной части.
3.Формирование адресной части:
–в адресную часть значение вершины;
–в вершину значение адреса нового элемента.
Алгоритм извлечения элемента из стека
Рассмотрим данный алгоритм с обработкой и уничтожением.
1. Используя вершину стека, выдавливаем последний занесенный элемент: t = begin;
2.Обрабатываем информационную часть (t ->i nfo) (выводим на экран).
3.Вершине стека присваиваем значение адресной части, т.е. вершину переставляем на следующий элемент: begin = t->Next;
4.Уничтожаем обработанный элемент, т.е. освобождаем занятую под него память: delete t;
Для первых двух элементов рассмотрим подробно:
begin=A2
i2 Next=A1 |
|
i1 NULL |
|
t
1)t = begin;
2)t -> info; например, печать i
3)begin = t -> Next; ,т.е. begin равен A1
begin
t |
|
|
i2 |
Next=A1 |
|
|
|
|
i1 |
NULL |
|
|
|
|
|
||||||
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
4)delete t;
А теперь давайте рассмотрим алгоритмы решения конкретного примера.
115

Задача. Сформировать стек, для простоты состоящий из целых положительных чисел (признаком окончания может быть отрицательное число, его в стек не заносим). Просмотреть стек, не изменяя вершины. Найти в стеке максимальный элемент и его порядковый номер, одновременно освобождая память, занятую просмотренным элементом.
В программе должна получиться следующая структура данных:
begin |
ik |
p |
|
ik-1 |
|
... |
|
|
i1 |
NULL |
|
|
|
|
|
|
|
|
|
|
|
Декларируем структурный тип данных глобально: struct Stack {
int info; Stack *Next; } ;
Формирование стека из k элементов
1.Декларируем объекты int i, kol = 0; где i – дополнительная переменная, kol – счетчик элементов;
Stack *begin = NULL, (стек пуст) *t;
2.Начало бесконечного цикла, например while(1)
а) считывание информации для текущего элемента (ввод i); б) если i<0 выход из цикла – break;
в) захват памяти под текущий элемент: t = new Stаck;
г) формирование информационной части t -> info = i;
д) значение вершины стека заносим в адресную часть текущего элемента (для первого элемента NULL):
t -> Next = begin;
е) вершину стека переставляем на начало только что созданного элемента: begin = t;
ж) счетчик элементов увеличиваем на единицу: kol++;
3.Конец цикла.
4.Вывод сообщения: «Kol elementov =… ».
Просмотр стека
1.Устанавливаем текущий указатель на вершину. Проверяем, не пуст ли стек. Если begin=NULL, то стек пуст (выдаем сообщение, и либо завершаем работу, либо переходим на его формирование).
2.Если стек не пуст начинаем цикл, выполняя до тех пор, пока t != NULL, т.е. не дошли до последнего.
а) выводим на печать информационную часть: printf(“\n Элемент: %d”, t -> info);
б) переставляем текущий указатель на следующий элемент:
116

t = t -> Next;
3. Конец цикла.
Поиск максимального элемента
1. Устанавливаем указатель текущего элемента на вершину: t = begin;
(можно проверить, не пустой ли стек).
2. Положим, что максимальный – это элемент вершины: int max = t -> info;
int nom_max = k; – номер элемента вершины, т.е. последнего. 3. Начало цикла, используем конструкцию do-while: do (выполнять)
а) begin = t -> Next; – переставляем вершину стека на следующий элемент, а указатель t остался на k-ом;
б) проверяем, если (t->info > max), заменяем max=t->info; и nom_max=k;
в) освобождаем память просмотренного элемента t : delete t;
г) t = begin; теперь и вспомогательный указатель устанавливаем на следующий элемент и уменьшаем номер текущего элемента: k--;
4.Конец цикла, выполняющегося до тех пор пока t != NULL.
5.Вывод на экран найденных значений max и nom_max.
Т.е. для данной конструкции:
1-ый прогон:
begin
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
... |
|
|
|
|
|
t |
|
|
|
ik |
|
pk |
|
|
|
|
|
|
ik-1 |
|
pk-1 |
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
k |
|
|
|
|
|
|
|
|
k-1 |
|
|
|
|
|
|
|
|
|
|
|
||
|
|
2-ой прогон: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
t |
|
|
|
|
|
|
|
begin |
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ik |
|
pk |
|
|
|
|
ik-1 |
|
|
pk-1 |
|
|
|
|
|
ik-2 |
|
pk-2 |
|
|
... |
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
k |
|
|
|
|
|
|
k-1 |
|
|
|
|
|
k-2 |
|
|
|
|
Завершится цикл тогда, когда p1 станет равно NULL (на предпоследнем ша-
ге Uk=NULL).
Текст программы может быть следующим:
#include <stdio.h> #include <conio.h>
struct Stack { // Описание структурного типа Stack int info;
Stack *Next;
117
} ; |
|
void main(void) { |
|
Stack *begin = NULL, *t; |
|
int i, kol = 0; |
|
while (1) { |
// Формирование стека |
printf(" Input info : "); |
|
scanf("%d", &i); |
|
if(i<0) break; |
// Выход из while(1) |
t = new Stack; |
|
t -> info = i; |
|
t -> Next = begin; |
|
begin = t; |
|
kol++; |
|
} |
|
|
printf("\n Kol elementov = %d", k); |
//====================== Просмотр стека =================== |
|
t = begin; |
// Установили текущий указатель на вершину if(t == NULL) |
{ |
|
printf("\n NO Steck!"); |
|
return; |
|
} |
|
i = k; |
|
while (t != NULL) { |
|
printf(“\n Element |
%d: %d”, i--, t -> info); |
t = t -> Next; |
// Текущий указатель на следующий элемент |
} |
|
//====================== Поиск максимума ================
t = begin; // Установили текущий указатель на вершину int max = t -> info;
int nom_max = k; do {
begin = t -> Next; if (t->info > max) {
max = t->info; nom_max = k;
} |
|
delete t; |
// Освобождаем память |
t = begin; |
|
k--; |
|
} while (t != NULL); |
|
printf(“\n Max = %d , number = %d ”, max, nom_max); getch();
}
118

Пример использования стека для проверки правильности расстановки скобок в арифметическом выражении.
Скобки расставлены верно, если:
а) число открывающихся и закрывающихся скобок совпадает; б) каждой открывающейся скобке соответствует закрывающаяся скобка.
Для выражения: ( А – ( В + С ) – D / ( A + C ) – нарушено первое условие, а для выражения: ( A – B ) + C ) – ( D / ( A + C ) – нарушено второе условие.
Алгоритм анализа следующий:
Просматриваем выражение слева направо.
1.Если обнаружена открывающая скобка, то записываем ее в стек.
2.Если первоначально обнаружена закрывающая скобка, то выражение составлено неверно, выводим сообщение об этом и завершаем работу.
3.Если теперь обнаружена открывающаяся скобка, то анализируем содержимое стека:
а) если стек пуст, то открывающая скобка отсутствует и выражение составлено неверно, выводим сообщение об этом и завершаем работу;
б) если стек не пуст, выбираем скобку – она открывающая, обе скобки выбрасываем из рассмотрения.
4.После того, как будет просмотрено все выражение, проверяем стек и, если он не пустой, то баланс скобок нарушен и выражение составлено неверно, выводим сообщение об этом и завершаем работу.
По такому принципу работают все компиляторы, проверяя арифметические выражения, вложенные блоки {...}, вложенные циклы и т.п.
Пример выражения: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
( x + ( y – ( a + b ) ) * c – ( d + e ) ) * k |
|
|
|
|
|
|
||||||||||
|
1 |
2 |
|
3 |
4 5 |
6 |
|
7 8 |
|
|
begin = NULL |
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
( |
|
( |
|
( |
|
|
( |
|
( |
|
( |
|
( |
|
|
|
|
|
|
|
|
( |
|
( |
|
|
( |
|
|
|
( |
|
|
|
|
|
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
2 |
|
3 |
|
|
4 |
|
5 |
|
6 |
|
7 |
9 |
|
|
119