
- •Часть 1
- •Общие сведения Сведения об эумк
- •Методические рекомендации по изучению дисциплины
- •Рабочая учебная программа
- •Учреждение образования
- •«Белорусский государственный университет
- •Информатики и радиоэлектроники»
- •Часть 2 __184__
- •Содержание дисциплины
- •1. Индивидуальные практические занятия, их характеристика
- •2. Контрольные работы, их характеристика
- •3. Курсовой проект, его характеристика
- •4. Литература
- •4.1. Основная
- •4.2. Дополнительная
- •5. Перечень компьютерных программ, наглядных и других пособий, методических указаний и материалов и технических средств обучения
- •Протокол согласования учЕбной программы по изучаемой учебной дисциплине с другими дисциплинами специальности
- •Теоретический раздел Введение
- •1. Основные типы данных
- •1.1. Общие сведения
- •1.2. Данные типа int
- •1.3. Данные типа char
- •1.4. Модификаторы доступа const и volatile
- •1.5. Данные вещественного типа (с плавающей точкой)
- •1.6. Элементарный ввод-вывод
- •1.7. Структура простой программы на языке Си
- •2. Операции и выражения
- •2.1. Выражение и его интерпретация
- •2.2. Основные операции
- •2.2.1. Арифметические операции
- •2.2.2. Побитовые логические операции
- •2.2.3. Операции сдвига
- •2.2.4. Операция присваивания
- •2.2.5. Операция sizeof
- •2.2.6. Преобразование типов в выражениях
- •2.2.7. Операция преобразования типов
- •2.2.8. Приоритеты в языке Си
- •3. Операторы управления вычислительным процессом
- •3.1. Оператор if
- •3.2. Операции отношения
- •3.3. Логические операции
- •3.4. Операция запятая
- •3.5. Операция условия ?:
- •3.6. Оператор безусловного перехода goto
- •3.7. Оператор switch
- •`` ` `3.8. Операторы цикла
- •3.8.1. Оператор for
- •3.8.2. Оператор while
- •3.8.3. Оператор do...While
- •3.9. Оператор break
- •3.10. Оператор continue
- •4. Массивы и указатели
- •4.1. Одномерные массивы и их инициализация
- •4.2. Многомерные массивы и их инициализация
- •4.3. Объявление указателей
- •4.4. Операции над указателями
- •1) Взятие адреса
- •2) Косвенная адресация или разыменование указателя
- •3) Увеличение или уменьшение значения указателя на целое число
- •4) Разность указателей
- •5) Сравнение указателей
- •6) Присваивание указателей друг другу
- •4.6. Связь между указателями и массивами
- •4.7. Динамическое распределение памяти
- •4.8. Массивы указателей
- •5. Функции
- •5.1. Общие сведения
- •5.2. Область видимости переменных
- •5.2.1. Локальные переменные
- •5.2.2. Глобальные переменные
- •5.3. Передача параметров в функцию
- •5.4. Рекурсивные функции
- •5.5. Использование функций в качестве параметров функций
- •5.6. Указатели на функции
- •5.7. Структура программы на Си
- •5.8. Передача параметров в функцию main()
- •6. Строки
- •7. Классы хранения и видимость переменных
- •7.1. Общие сведения
- •7.2. Автоматический класс хранения (auto)
- •7.3. Регистровый класс хранения (register)
- •7.4. Статический класс хранения (static)
- •7.5. Внешний класс хранения (extern)
- •7.6. Заключение
- •8. Структуры, объединения и перечисления
- •8.1. Общие сведения
- •8.2. Инициализация структурных переменных
- •8.3. Вложенные структуры
- •8.4. Указатели на структуры
- •8.5. Массивы структурных переменных
- •8.6. Передача функциям структурных переменных
- •8.7. Оператор typedef
- •8.8. Поля битов в структурах
- •8.9. Объединения
- •8.10. Перечисления
- •9. Динамические структуры данных
- •9.1. Общие сведения
- •9.2. Связные списки
- •9.2.1. Односвязные списки
- •9.2.2. Двусвязные списки
- •9.2.3. Циклические списки
- •9.3. Стеки
- •9.4. Очереди
- •9.5. Деревья
- •9.5.1. Понятие графа
- •9.5.2. Бинарные деревья
- •10. Файлы
- •10.1. Общие сведения
- •10.2. Открытие и закрытие файлов
- •10.3. Функции ввода-вывода для работы с текстовыми файлами
- •10.4. Произвольный доступ к файлу
- •10.5. Функции ввода-вывода для работы с бинарными файлами
- •11. Директивы препроцессора
- •11.1. Основные понятия
- •11.2. Директива #include
- •11.3. Директивы препроцессора #define и #undef
- •11.3.1. Символические константы
- •11.3.2. Макросы с параметрами
- •11.3.3. Директива #undef
- •11.4. Условная компиляция
- •11.5. Директивы # и ##
- •12. Модульное программирование
- •13. Введение в объектно-ориентированное программирование
- •13.1. Постановка задачи
- •13.2. Решение задачи средствами Си
- •13.5. Наследование
- •13.6. Перегрузка
- •13.7. Ссылочный тип
- •Литература
- •Приложение 1. Рекомендации по оформлению текстов программ
- •Тесты к теоретическому разделу Вопросы к разделу 1. Основные типы данных
- •Вопросы к разделу 2. Операции и выражения
- •Вопросы к разделу 3. Операторы управления вычислительным процессом
- •Вопросы к разделу 4. Массивы и указатели
- •Вопросы к разделу 5. Функции
- •Вопросы к разделу 6. Строки
- •Вопросы к разделу 7. Классы хранения и видимость переменных
- •Вопросы к разделу 8. Структуры, объединения и перечисления
- •Вопросы к разделу 9. Динамические структуры данных
- •Вопросы к разделу 10. Файлы
- •Вопросы к разделу 11. Директивы препроцессора
- •Вопросы к разделу 12. Модульное программирование
- •Вопросы к разделу 13. Введение в ооп
- •Правильные ответы на вопросы тестов к теоретическому разделу
- •Вопросы к теоретическому зачету
- •Варианты индивидуальных заданий
- •Контрольная работа №2
- •Варианты индивидуальных заданий
- •Индивидуальные практические работы Указания к выбору варианта индивидуальных практических работ
- •Индивидуальная практическая работа № 1. Массивы и строки
- •Варианты индивидуальных заданий
- •Индивидуальная практическая работа № 2. Динамические структуры данных
- •Варианты индивидуальных заданий
8.10. Перечисления
Перечисляемый тип представляет собой упорядоченное множество элементов, доступ к которым осуществляется по имени. Этот тип позволяет улучшить читаемость текста программы.
Синтаксис для задания данных переменных подобен синтаксису объявления структурной переменной и в общем виде выглядит так:
enum имя_типа_перечисл {список_перечисл} список_переменных;
enum month{Jan,Feb,Mar,...}; // объявляется перечисление
// с именем month, в фигурных
// скобках – возможные
// значения данной переменной
enum month m1; // m1 объявляется в качестве
// переменной типа month
m1 = Jan; // m1 присваивается Jan
Элементы списка, указанные в шаблоне, представляют собой константы, которые обозначаются идентификатором. Им соответствуют неявно значения, начиная с 0, с последующим увеличением на 1. Выражение в шаблоне можно проинициализировать, т.е. назначить отдельному элементу перечисления явно какое-либо значение.
Число байт, отводимое под переменную перечисления, зависит от количества и значений констант в шаблоне.
Если все значения констант могут быть представлены значениями типа unsigned char, то под переменную m1 отведется 1 байт, если не хватает – 2 байта.
Имя переменной может использоваться в выражениях, например
if ((m1 < Mar) || (m1 > Nov))
printf(“Winter”);
else
{
if (m1 < Jun)
printf(“Spring”);
else
{
if (m1 < Sep)
printf(“Summer”);
else
printf(“Autumn”);
}
}
9. Динамические структуры данных
9.1. Общие сведения
В этой главе вводится понятие динамических структур данных. Это такие структуры данных, которые не являются строго ограниченными по размеру, как, например, массивы или структуры, а увеличиваются или уменьшаются в процессе выполнения программы. Примером таких структур являются связные списки, стеки, очереди, графы, деревья. Все они будут рассмотрены подробнее далее.
Создание динамических структур требует динамического распределения памяти. Это дает возможность заранее не учитывать объем требуемой памяти, а в процессе выполнения программы увеличивать ее объем для хранения добавленных элементов или освобождать при их удалении.
В Си для этого существуют функции calloc, malloc и free.
Функции malloc и calloc служат для выделения памяти в динамической куче, а free – для ее освобождения.
9.2. Связные списки
9.2.1. Односвязные списки
Список – это линейная динамическая структура данных, число элементов которой может изменяться по мере их добавления и/или удаления.
Список состоит из элементов, называемых узлами. Каждый узел содержит в себе указатель на адрес, где хранится следующий элемент списка. Доступ к списку осуществляется через внешний указатель, который объявляется в программе и указывает на первый элемент. Указатель на первый элемент списка называется головой, а последний – хвостом.
Следует отметить, что узлы связных списков обычно не хранятся в памяти друг за другом, как элементы массива.
В зависимости от организации связи между узлами списка различают его различные виды.
Однонаправленный связный список представляет собой последовательность связанных между собой узлов, каждый из которых состоит из поля данных и указателя на место в памяти, где расположен следующий элемент, указатель последнего элемента списка имеет значение NULL.
На рис. 9.1 приведен пример односвязного направленного списка. Узел данного списка состоит из поля данных element типа int и указателя на следующий элемент NextPtr, указатель на первый элемент – HeadPtr.
Структура, описывающая создаваемый список:
struct spisok
{
int element;
struct spisok *nextPtr;
};
Рис. 9.1. Односвязный список
Если список пуст, то указатель на список (headPtr) будет содержать NULL, и в этом случае список попросту будет заполняться в том порядке, в котором будут вводиться данные. Ниже приведена программа по созданию данного списка.
// Программа, демонстрирующая создание односвязного списка
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
// структура, описывающая создаваемый список
struct spisok
{
int element;
struct spisok *nextPtr;
};
typedef spisok Spisok;
typedef Spisok *SpisokPtr;
void createSpisok(SpisokPtr *firstPtr); // описание прототипа
// функции создания
void main()
{
clrscr();
SpisokPtr headPtr = NULL;
int a;
printf(“Vvedite elementi:\n”);
createSpisok(&headPtr);
}
// функция создания односвязного списка
// в качестве параметра получает указатель на начало списка
void createSpisok(SpisokPtr *firstPtr)
{
SpisokPtr newPtr;
int value;
scanf(“%d”, &value);
// выделение памяти для нового элемента списка
newPtr = (Spisok *) malloc(sizeof(Spisok));
if ((newPtr != NULL) && (value != 1))
{
newPtr->element = value;
newPtr->nextPtr = *firstPtr; // список сдвигается на один
// элемент, путем
*firstPtr = newPtr; // вставки на первое
// место нового элемента
// рекурсивный вызов функции создания
createSpisok(&((*firstPtr)->nextPtr));
}
else
printf(“Error!”);
}
Для того чтобы вставить новый элемент в уже имеющийся список, необходимо разорвать связь между узлами списка, куда вставляется новый элемент. Для этого указателю узла, расположенного перед вставляемым элементом, необходимо присвоить значение адреса, по которому была выделена память для элемента, и в свою очередь, указателю, содержащемуся во вставляемом узле, присвоить адрес узла, расположенного после вставляемого элемента (рис. 9.2).
Рис. 9.2. Вставка элемента в список
// Функция вставки элемента в список
void insertElement(SpisokPtr *firstPtr, int value)
{
SpisokPtr newPtr, previousPtr, workPtr;
newPtr = (Spisok *) malloc(sizeof(Spisok));
if (newPtr != NULL)
{
newPtr->element = value;
newPtr->nextPtr = NULL;
workPtr = *firstPtr; // некоторому рабочему указателю
// присваивается значение указателя
// на первый элемент списка
previousPtr = NULL; // значение предыдущего указателя
// равно NULL
while (workPtr != NULL && value < workPtr->element)
{
previousPtr = workPtr; // происходит продвижение
// по списку
workPtr = workPtr->nextPtr;
}
previousPtr->nextPtr = newPtr; // вставка нового элемента
// в список
newPtr->nextPtr = workPtr;
}
else
printf("Error!");
}
Для удаления элемента необходимо проделать обратную операцию, присвоив указателю узла, расположенного перед удаляемым элементом, значение адреса узла, расположенного в списке после удаляемого элемента. В качестве параметров функция получает указатель на первый элемент списка, а также значение, определяющее удаляемый элемент.
// Функция удаления элемента из списка
void deleteElement(SpisokPtr *firstPtr, int value)
{
SpisokPtr newPtr, predPtr, workPtr;
if (*firstPtr != NULL)
{
workPtr = *firstPtr;
predPtr = NULL;
//пока не конец списка или не найден искомый элемент
while (workPtr != NULL && value < workPtr->element)
{
predPtr = workPtr; //перемещение по списку
workPtr = workPtr->nextPtr;
}
if (predPtr != NULL)
{
// если удаляемый элемент находится не в начале списка
predPtr->nextPtr = workPtr->nextPtr;
free(workPtr);
}
else
{
predPtr = *firstPtr; // если в начале
*firstPtr = (*firstPtr)->nextPtr;
free(predPtr);
}
}
else
printf("Error!");
}