Перейти к содержанию

Билеты и ответы на них

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 — это мощный инструмент для работы с операционной системой.

Вторая лекция

Основные этапы решения задач на ЭВМ

  • Этапы решения задач включают:
    1. Формулировка задачи — чёткое понимание того, что нужно решить. Например, если задача – написать программу для сортировки массива, нужно определить, какой тип сортировки использовать, в каком порядке должны быть элементы, и что делать с дубликатами.
    2. Разработка алгоритма — последовательность действий для решения задачи. Тут нужно продумать последовательность действий, которые приведут к решению. Можно использовать блок-схемы или псевдокод, чтобы визуализировать алгоритм.
    3. Программирование — реализация алгоритма на конкретном языке программирования. На этом этапе мы переводим алгоритм на язык Си, используя переменные, операторы, функции и т.д.
    4. Тестирование и отладка — проверка корректности работы программы и устранение ошибок. Здесь мы проверяем, правильно ли работает программа, и ищем ошибки. Можно использовать отладчик, чтобы пошагово выполнять код и наблюдать за значениями переменных.
    5. Оптимизация — улучшение работы программы с точки зрения производительности и использования памяти. Если программа работает слишком медленно или занимает много памяти, ее можно оптимизировать. Например, использовать более эффективные алгоритмы или структуры данных.

Алгоритм и его свойства

  • Алгоритм — это последовательность чётких, конечных шагов для выполнения задачи. Основные свойства алгоритма:
    • Конечность — алгоритм должен завершаться за конечное количество шагов. Алгоритм не должен зацикливаться. Например, алгоритм поиска максимального элемента в массиве должен завершиться после проверки всех элементов.
    • Определённость — каждый шаг алгоритма должен быть чётко описан. Каждый шаг алгоритма должен быть однозначным. Например, инструкция «сложить два числа» — определённая, а инструкция «сделать что-нибудь» — нет.
    • Результативность — алгоритм должен давать правильный результат. Алгоритм должен приводить к правильному результату. Например, алгоритм сортировки должен упорядочивать элементы в нужном порядке.
    • Эффективность — алгоритм должен использовать минимальные ресурсы (время и память). Алгоритм должен быть оптимальным по времени и памяти. Например, для поиска элемента в отсортированном массиве лучше использовать бинарный поиск, а не линейный.

Третья лекция

Общая характеристика языка Си

  • Язык Си — это процедурный язык программирования, который позволяет работать как с низкоуровневыми, так и с высокоуровневыми задачами. Он предоставляет широкие возможности для манипуляций с памятью и позволяет работать с указателями, что дает программисту большую гибкость и контроль над выполнением программы. Си — это эффективный и гибкий язык, который дает программисту большой контроль над программой.

Структура программы, написанной на языке Си

  • Структура программы на Си включает:
    • Заголовочные файлы — включают библиотеки и внешние определения, такие как #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().

Динамическое выделение памяти для двумерных массивов

  • Для двумерных массивов используется динамическое выделение памяти с помощью указателей на указатели.