Билеты и ответы на них
Warning
Это предполагаемые билеты по программированию. Последнее обновление - 12 января в 15:00.
Первая лекция
Цели и задачи дисциплины. Место дисциплины в структуре образовательной программы
- Цель дисциплины — изучение основ программирования, включающее освоение алгоритмов, структуры данных и синтаксиса языка программирования Си. Задачи дисциплины — обучение базовым принципам разработки программ, знакомство с инструментами разработки, отладкой и тестированием программ. Дисциплина даст необходимые знания и навыки для создания программ на языке Си, которые пригодятся вам при выполнении лабораторных работ.
- Место дисциплины в структуре образовательной программы: Дисциплина является основой для дальнейшего изучения более специализированных курсов по программированию, операционным системам, базам данных и алгоритмам. Программирование является важным компонентом в подготовке специалистов в области информационных технологий и инженерии. Знания, полученные в рамках этой дисциплины, будут необходимы для успешного освоения других предметов.
Язык программирования Си. Историческая справка
- Язык Си был разработан Деннисом Ритчи в 1970-х годах в лабораториях AT&T Bell Labs для разработки операционной системы UNIX. В разработке также участвовал Кен Томпсон. Си быстро стал популярным благодаря своей мощности, гибкости и низкоуровневым возможностям, что позволило разрабатывать как системное, так и прикладное программное обеспечение. Язык Си оказал значительное влияние на дальнейшие языки программирования, такие как C++, Java и другие. Си — это один из самых влиятельных языков программирования в истории. Первый стандарт языка был опубликован в 1989 году (ANSI C) и был основан на работе Брайана Кернигана и Денниса Ритчи.
- Брайан Керниган — канадский учёный в области информатики, известный своим вкладом в развитие языка Си. Он является соавтором первой книги по языку Си ("Язык программирования Си"), которая стала классическим учебником по программированию.
Интегрированные среды разработки программ
- Интегрированные среды разработки (IDE) — это программы, которые объединяют все необходимые инструменты для разработки, компиляции и отладки программ. В таких средах можно писать код, компилировать его, тестировать и отлаживать все в одном месте. Примеры популярных IDE для разработки на Си: Visual Studio, Code::Blocks, CLion. Эти среды существенно облегчают работу разработчика, предоставляя поддержку синтаксиса, автозавершение, встроенные отладчики и прочее. IDE позволяют писать код быстрее и эффективнее, благодаря набору полезных инструментов.
Интерфейс командной строки (CLI)
- Интерфейс командной строки (CLI) — это текстовый интерфейс, через который пользователи взаимодействуют с операционной системой. В программировании на Си часто используется CLI для компиляции программ (например, с помощью команды
gcc
) и для запуска программ. CLI позволяет быстро выполнять команды и манипулировать файлами и процессами на компьютере. CLI — это мощный инструмент для работы с операционной системой.
Вторая лекция
Основные этапы решения задач на ЭВМ
- Этапы решения задач включают:
- Формулировка задачи — чёткое понимание того, что нужно решить. Например, если задача – написать программу для сортировки массива, нужно определить, какой тип сортировки использовать, в каком порядке должны быть элементы, и что делать с дубликатами.
- Разработка алгоритма — последовательность действий для решения задачи. Тут нужно продумать последовательность действий, которые приведут к решению. Можно использовать блок-схемы или псевдокод, чтобы визуализировать алгоритм.
- Программирование — реализация алгоритма на конкретном языке программирования. На этом этапе мы переводим алгоритм на язык Си, используя переменные, операторы, функции и т.д.
- Тестирование и отладка — проверка корректности работы программы и устранение ошибок. Здесь мы проверяем, правильно ли работает программа, и ищем ошибки. Можно использовать отладчик, чтобы пошагово выполнять код и наблюдать за значениями переменных.
- Оптимизация — улучшение работы программы с точки зрения производительности и использования памяти. Если программа работает слишком медленно или занимает много памяти, ее можно оптимизировать. Например, использовать более эффективные алгоритмы или структуры данных.
Алгоритм и его свойства
- Алгоритм — это последовательность чётких, конечных шагов для выполнения задачи. Основные свойства алгоритма:
- Конечность — алгоритм должен завершаться за конечное количество шагов. Алгоритм не должен зацикливаться. Например, алгоритм поиска максимального элемента в массиве должен завершиться после проверки всех элементов.
- Определённость — каждый шаг алгоритма должен быть чётко описан. Каждый шаг алгоритма должен быть однозначным. Например, инструкция «сложить два числа» — определённая, а инструкция «сделать что-нибудь» — нет.
- Результативность — алгоритм должен давать правильный результат. Алгоритм должен приводить к правильному результату. Например, алгоритм сортировки должен упорядочивать элементы в нужном порядке.
- Эффективность — алгоритм должен использовать минимальные ресурсы (время и память). Алгоритм должен быть оптимальным по времени и памяти. Например, для поиска элемента в отсортированном массиве лучше использовать бинарный поиск, а не линейный.
Третья лекция
Общая характеристика языка Си
- Язык Си — это процедурный язык программирования, который позволяет работать как с низкоуровневыми, так и с высокоуровневыми задачами. Он предоставляет широкие возможности для манипуляций с памятью и позволяет работать с указателями, что дает программисту большую гибкость и контроль над выполнением программы. Си — это эффективный и гибкий язык, который дает программисту большой контроль над программой.
Структура программы, написанной на языке Си
- Структура программы на Си включает:
- Заголовочные файлы — включают библиотеки и внешние определения, такие как
#include <stdio.h>
. Заголовочные файлы содержат определения функций и других элементов, которые используются в программе. - Функция
main()
— точка входа программы, где начинается выполнение. Функцияmain()
— это главная функция программы, с которой начинается выполнение. - Директивы препроцессора — инструкции, которые обрабатываются до компиляции (например,
#define
). Директивы препроцессора позволяют управлять процессом компиляции. - Функции — блоки кода, которые выполняют конкретные задачи. Функции позволяют разбивать программу на более мелкие и управляемые части.
- Заголовочные файлы — включают библиотеки и внешние определения, такие как
Директивы препроцессора
- Препроцессор обрабатывает директивы перед компиляцией исходного кода. Основные директивы:
#include
— подключение внешних заголовочных файлов.#include
используется для подключения заголовочных файлов, содержащих определения функций и других элементов.#define
— определение макросов.#define
позволяет определять макросы, которые заменяются препроцессором перед компиляцией. В лабораторной работе №3 директива#define
используется для определения константыMAX_LEN
.#ifdef
,#endif
— условная компиляция, позволяющая включать или исключать части кода в зависимости от условий. Условная компиляция позволяет создавать разные версии программы из одного исходного кода.
Понятие о функции. Простейшие средства ввода-вывода
- Функция в языке Си — это блок кода, выполняющий конкретную задачу. Функции могут принимать параметры, выполнять операции и возвращать результаты. Функции — это основные строительные блоки программ на Си.
- Простейшие средства ввода-вывода включают:
printf()
— для вывода данных на экран.printf()
используется для вывода текста и значений переменных на экран.scanf()
— для ввода данных с клавиатуры.scanf()
используется для ввода данных с клавиатуры.
Четвёртая лекция
Тип данных (тип)
- Тип данных определяет, какой вид данных может быть сохранён в переменной и какие операции над ними можно выполнять. Примеры типов данных в языке Си:
int
— целое число.int
используется для хранения целых чисел.float
— число с плавающей запятой.float
используется для хранения чисел с плавающей запятой (дробных чисел).char
— символ.char
используется для хранения символов.double
— число с двойной точностью.double
используется для хранения чисел с плавающей запятой с большей точностью.
Переменные и константы
- Переменная — это область памяти, которая хранит изменяемое значение.
- Константа — это значение, которое не может быть изменено во время выполнения программы.
- В Си переменные и константы объявляются с указанием типа:
int x = 10; // Переменная const int y = 20; // Константа
Преобразование типов
- Преобразование типов используется для изменения типа данных. Это необходимо, когда разные типы данных нужно использовать в одном выражении. Существует два вида преобразования типов:
- Явное преобразование: программист явно указывает, к какому типу нужно преобразовать данные. Например:
float f = 3.14; int i = (int)f; // Явное преобразование float в int
- Неявное преобразование: компилятор автоматически преобразует данные к нужному типу. Например:
int a = 10; float b = 3.14; float c = a + b; // Неявное преобразование int в float
- Явное преобразование: программист явно указывает, к какому типу нужно преобразовать данные. Например:
Составные типы и указатели
- Составные типы в Си включают структуры (
struct
), объединения (union
) и перечисления (enum
). Они позволяют комбинировать различные типы данных в одну структуру. Составные типы позволяют создавать более сложные структуры данных. В лабораторной работе №10 используются структуры для хранения информации о товарах. - Указатели — это переменные, которые хранят адреса других переменных. Указатели используются для работы с динамической памятью и эффективной передачи данных в функции. Указатели — это мощный инструмент в Си, который позволяет работать с памятью напрямую.
Пятая лекция
Операции
- Операции — это действия, которые выполняются над операндами. В языке Си существуют арифметические операции, логические операции и побитовые операции. Примеры:
- Арифметические:
+
,-
,*
,/
,%
. Арифметические операции используются для выполнения математических действий. - Логические:
&&
,||
,!
. Логические операции используются для работы с логическими значениями (истина и ложь).
- Арифметические:
Операторы
- Операторы — это символы, которые обозначают операции. Примеры:
- Операторы присваивания:
=
,+=
,-=
. Операторы присваивания используются для присвоения значений переменным. - Операторы сравнения:
==
,!=
,<
,>
. Операторы сравнения используются для сравнения значений. - Операторы логического И/ИЛИ:
&&
,||
. Операторы логического И/ИЛИ используются для комбинирования логических условий.
- Операторы присваивания:
Выражения
- Выражение — это комбинация операндов и операторов, которая возвращает значение. Например:
int a = 5, b = 10; int sum = a + b; // Выражение a + b
Приведение типов в выражениях
- Приведение типов используется для явного преобразования одного типа данных в другой в рамках выражений:
float f = 5.7; int i = (int)f; // Преобразуем float в int
Шестая лекция
Ввод-вывод в ЭВМ
- Ввод-вывод (I/O) — это процесс взаимодействия программы с пользователем или внешними устройствами. В языке Си для этого используются функции
scanf()
иprintf()
для ввода и вывода данных, а также функции для работы с файлами. Ввод-вывод позволяет программам взаимодействовать с окружающим миром.
Средства ввода-вывода в языке Си
- В Си стандартные средства ввода и вывода — это функции
scanf()
для ввода иprintf()
для вывода.fopen()
— для открытия файла.fopen()
используется для открытия файла для чтения или записи.fclose()
— для закрытия файла.fclose()
используется для закрытия файла после завершения работы с ним.fscanf()
,fprintf()
— для чтения и записи в файл.fscanf()
иfprintf()
используются для чтения и записи данных в файл.
Высокоуровневый файловый ввод-вывод с использованием стандартной библиотеки
- Стандартная библиотека Си предоставляет простые функции для работы с файлами, которые абстрагируют детали низкоуровневого ввода-вывода, делая код более читаемым и удобным. Высокоуровневые функции упрощают работу с файлами.
Низкоуровневый интерфейс языка Си
- Низкоуровневый интерфейс включает работу с файловыми дескрипторами через системные вызовы, такие как
open()
,read()
,write()
,close()
. Эти функции позволяют выполнять более гибкие и контролируемые операции ввода-вывода. Низкоуровневый интерфейс дает больше контроля над операциями ввода-вывода.
Седьмая лекция
Классификация инструкций языка Си
- В языке Си инструкции можно классифицировать на несколько типов:
- Операторы — это команды, которые выполняют действия с переменными (например, присваивание, арифметические операции). Операторы изменяют значения переменных или выполняют другие действия.
- Инструкции выбора — используются для принятия решений в программе (например,
if
,switch
). Инструкции выбора позволяют выполнять разные блоки кода в зависимости от условий. - Инструкции цикла — позволяют многократно выполнять блок кода (например,
for
,while
). Инструкции цикла позволяют повторять блок кода до тех пор, пока выполняется условие. В лабораторной работе №4 используются циклыfor
для вычисления суммы и произведения чисел. - Инструкции перехода — изменяют порядок выполнения программы (например,
break
,continue
,return
). Инструкции перехода позволяют изменять порядок выполнения инструкций в программе.
Инструкции выбора if
, switch
- Оператор
if
используется для выполнения блока кода, если условие истинно. Он имеет форму:if (условие) { // блок кода }
- Оператор
switch
позволяет выбрать один из множества блоков кода, основываясь на значении выражения. Он используется, когда есть несколько возможных вариантов:switch (переменная) { case 1: // код для случая 1 break; case 2: // код для случая 2 break; default: // код для случая по умолчанию }
Операторы цикла
- В языке Си три основных оператора цикла:
for
— используется для выполнения кода заданное количество раз. Пример:for (int i = 0; i < 5; i++) { // цикл выполняется 5 раз }
while
— выполняет блок кода, пока условие истинно:while (условие) { // цикл выполняется до тех пор, пока условие истинно }
do-while
— выполняет блок кода хотя бы один раз, а затем проверяет условие:do { // блок кода выполняется хотя бы один раз } while (условие);
Операторы перехода и возврата
break
— завершает выполнение цикла или блокаswitch
.break
используется для преждевременного выхода из цикла или блокаswitch
.continue
— пропускает текущую итерацию цикла и переходит к следующей.continue
используется для пропуска текущей итерации цикла и перехода к следующей.return
— завершает выполнение функции и может возвращать значение.return
используется для завершения выполнения функции и возврата значения.
Восьмая лекция
Функции в Си
- Функции — это блоки кода, которые выполняют конкретную задачу. Они могут принимать параметры, выполнять вычисления и возвращать результат. Это позволяет избежать дублирования кода и повышает читаемость программы. Функции — это важный инструмент для структурирования программ и повышения их читаемости.
- Основная структура функции:
return_type function_name(parameters) { // Тело функции }
Формальные и фактические параметры
- Формальные параметры — это переменные, указанные в определении функции, которые используются в теле функции. Формальные параметры — это переменные, которые объявляются в заголовке функции.
- Фактические параметры — это значения или переменные, передаваемые в функцию при её вызове.
- Например:
int add(int a, int b) { return a + b; // a и b — формальные параметры } int result = add(3, 4); // 3 и 4 — фактические параметры
Передача массивов в функцию
- Массивы передаются в функцию как указатели на первый элемент массива. Изменения, сделанные в массиве внутри функции, отразятся на оригинальном массиве, так как передаётся не сам массив, а его адрес. При передаче массива в функцию передается указатель на его первый элемент.
- Пример:
void modify_array(int arr[], int size) { for (int i = 0; i < size; i++) { arr[i] *= 2; // Удваиваем каждый элемент массива } }
Функции с переменным числом параметров
- Для функций с переменным числом параметров в Си используется библиотека
stdarg.h
. Это позволяет передавать в функцию нефиксированное количество аргументов. Функции с переменным числом параметров позволяют создавать более гибкие функции. - Пример:
#include <stdarg.h> int sum(int count, ...) { va_list args; va_start(args, count); int total = 0; for (int i = 0; i < count; i++) { total += va_arg(args, int); } va_end(args); return total; }
Функции. Что ещё?..
- Функции могут быть рекурсивными, то есть вызывать сами себя для решения задачи. Рекурсия — это мощный инструмент для решения задач, которые могут быть разбиты на более мелкие подзадачи того же типа. В лабораторной работе №5 используется рекурсивная функция для вычисления факториала числа.
- Также они могут возвращать указатели и использовать статическую память для хранения значений между вызовами. Функции могут возвращать указатели на данные в памяти. Статическая память используется для хранения значений переменных между вызовами функции.
Девятая лекция
Память компьютера
- Память компьютера делится на несколько областей:
- Оперативная память (RAM) — используется для хранения данных и программ, которые активно используются процессором.
- Постоянная память (ROM) — хранит данные, которые не изменяются, например, BIOS.
- Кэш-память — быстрое хранилище для часто используемых данных, доступ к которым требуется быстрее, чем из основной памяти.
- Регистры процессора — самые быстрые и ограниченные области памяти, используемые процессором для хранения промежуточных данных.
Область действия переменных
- Переменные могут иметь различную область действия:
- Глобальная область — переменная доступна в любой части программы. Глобальные переменные доступны в любой части программы.
- Локальная область — переменная доступна только в той функции или блоке кода, где она объявлена. Локальные переменные доступны только в той функции или блоке кода, где они объявлены.
- Статическая область — переменная сохраняет своё значение между вызовами функции. Статические переменные сохраняют свое значение между вызовами функции.
Классы памяти
- Статическая память — используется для хранения глобальных и статических переменных. Статическая память выделяется при запуске программы и освобождается при ее завершении.
- Стек — используется для хранения локальных переменных и данных о вызовах функций.
- Куча — область памяти для динамического выделения памяти с помощью функций
malloc()
,calloc()
,realloc()
.
Объявление и определение переменной
- Объявление переменной сообщает компилятору о её типе и имени, в то время как определение выделяет память для переменной.
Класс памяти для функций
- Для функций выделяется память в стеке. Параметры и локальные переменные сохраняются в стеке во время выполнения функции. При возврате из функции стек очищается.
Десятая лекция
Понятие массива. Одномерные массивы
- Массив — это структура данных, состоящая из элементов одного типа, индексируемых целыми числами. Массивы в Си представляют собой последовательности элементов, которые хранятся в памяти подряд. Массивы позволяют хранить большое количество данных одного типа. В лабораторной работе №6 используются одномерные массивы для хранения чисел и выполнения различных операций над ними.
- Пример: В Си массив — это фактически указатель на его первый элемент. Имя массива — это указатель на его первый элемент.
int arr[5] = {1, 2, 3, 4, 5};
Связь указателей и массивов
- Массивы и указатели тесно связаны: имя массива является указателем на его первый элемент. Таким образом, доступ к элементам массива можно осуществлять через указатели:
int *ptr = arr; // ptr указывает на первый элемент массива
Строки как одномерные массивы данных типа char
- Строки в Си представляют собой массивы символов, заканчивающиеся нулевым символом
'\0'
. Строки можно обрабатывать как массивы:char str[] = "Hello"; printf("%s", str); // Выводит строку "Hello"
Примеры обработки массивов
- Массивы часто используются для хранения данных, таких как числа, строки или другие структуры. Пример:
int arr[] = {1, 2, 3, 4, 5}; for (int i = 0; i < 5; i++) { arr[i] *= 2; // Удваиваем каждый элемент массива }
Одиннадцатая лекция
Указатели в языке Си
- Указатель — это переменная, которая хранит адрес другой переменной. Указатели позволяют работать с памятью напрямую, что делает программу более эффективной и гибкой. Указатели — это мощный инструмент в Си, который позволяет работать с памятью напрямую. Пример:
int a = 10; int *ptr = &a; // Указатель на переменную a
Операции над указателями
- Операции с указателями включают:
- Разыменование: доступ к значению, на которое указывает указатель:
*ptr
. - Арифметика указателей: указатели можно увеличивать или уменьшать, чтобы переходить по памяти. Арифметика указателей позволяет перемещаться по памяти с помощью указателей.
- Разыменование: доступ к значению, на которое указывает указатель:
Нетипизированный указатель
void*
— это указатель, который может указывать на любую переменную. Он часто используется для создания универсальных функций. Нетипизированные указатели могут указывать на данные любого типа. Пример:void *ptr; int a = 5; ptr = &a;
Указатели и const
- Указатель на
const
гарантирует, что данные, на которые он указывает, не будут изменены:const int *ptr = &a;
Указатель на указатель
- Указатель на указатель — это переменная, которая хранит адрес другого указателя:
int **ptr2 = &ptr;
Указатель файла
- Для работы с файлами используется указатель типа
FILE*
. Это позволяет открывать, читать, записывать и закрывать файлы. Указатель файла используется для работы с файлами.
Указатели и массивы
- Массивы в Си являются указателями на их первый элемент, что позволяет работать с массивами через указатели. Массивы и указатели тесно связаны в Си. В лабораторной работе №7 используются указатели для передачи массивов в функции.
Указатели на функции
- Указатели на функции позволяют динамически передавать функции в другие функции или структуры данных:
int add(int x, int y) { return x + y; } int (*func_ptr)(int, int) = add; printf("%d", func_ptr(3, 4)); // Выводит 7
Двенадцатая лекция
Указатели и массивы
- Как мы уже знаем, массивы в Си тесно связаны с указателями. Имя массива — это фактически указатель на его первый элемент. Это позволяет использовать указатели для доступа к элементам массива и эффективной обработки массивов.
Массив указателей
- Массив указателей — это массив, каждый элемент которого является указателем. Это позволяет хранить указатели на различные объекты, например, на строки разной длины.
- Пример:
char *strings[] = {"one", "two", "three"}; // Массив указателей на строки
Строки
- Напомним, что строки в Си — это массивы символов, заканчивающиеся нулевым символом
'\0'
. С помощью указателей можно обрабатывать строки, например, копировать или сравнивать их. В лабораторной работе №8 используются указатели для работы со строками, например, для их конкатенации.
Примеры обработки массивов
- Вот пример, как можно использовать указатель для вывода строки на экран:
char str[] = "Hello"; char *ptr = str; // Указатель на первый символ строки while (*ptr != '\0') { putchar(*ptr); // Выводим текущий символ ptr++; // Переходим к следующему символу }
Тринадцатая лекция
Двумерные массивы
- Двумерные массивы — это массивы массивов, которые могут быть представлены как таблицы. Каждый элемент массива имеет два индекса: строку и столбец. Двумерные массивы позволяют хранить данные в виде таблицы. В лабораторной работе №9 используются двумерные массивы для хранения матриц и выполнения различных операций над ними. Пример:
int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
Операции с двумерными массивами
- Операции с двумерными массивами включают доступ к элементам, а также итерацию по строкам и столбцам массива. Двумерные массивы могут быть обработаны с помощью циклов.
Сортировка в массиве
- Для сортировки массивов используются различные алгоритмы, такие как сортировка пузырьком, сортировка выбором и другие. Существует много алгоритмов сортировки массивов.
Поиск в массиве
- Для поиска элементов в массиве можно использовать линейный поиск или бинарный поиск (если массив отсортирован). Линейный поиск проверяет каждый элемент массива, а бинарный поиск может быть использован только для отсортированных массивов.
Связь двумерных массивов с указателями
- Двумерные массивы можно представлять как массив указателей, что позволяет работать с ними через указатели на указатели. Двумерные массивы могут быть представлены как массивы указателей.
Четырнадцатая лекция
Динамическая память
- Динамическая память используется для выделения памяти в процессе выполнения программы. Для работы с динамической памятью в Си используются функции
malloc()
,calloc()
,realloc()
иfree()
.
Функции для динамического распределения памяти
malloc()
— выделяет память для заданного количества байт.calloc()
— выделяет память и инициализирует её нулями.realloc()
— изменяет размер ранее выделенного блока памяти.free()
— освобождает ранее выделенную память.free()
освобождает память, выделенную с помощьюmalloc()
,calloc()
илиrealloc()
.
Использование функций malloc и free
- Пример выделения и освобождения памяти:
int *arr = (int*)malloc(5 * sizeof(int)); free(arr);
Одномерные динамические массивы
- Одномерные динамические массивы создаются с помощью
malloc()
и освобождаются с помощьюfree()
.
Динамическое выделение памяти для двумерных массивов
- Для двумерных массивов используется динамическое выделение памяти с помощью указателей на указатели.