Перейти к содержимому
Topos Uranos

Краткое содержание

Этот урок открывает первый раздел курса, посвящённый первым шагам в программировании на C++. Он объясняет, для чего нужен раздел: провести того, кто никогда не программировал, от задачи, сформулированной обычным языком, до программы, которая компилируется, запускается, считывает данные и выводит результаты, причём так, чтобы было понятно, что сделал на этом пути каждый инструмент; представляет шесть уроков раздела и нить, которая их связывает, от алгоритма, записанного по-русски, до классификации ошибок; уточняет то немногое, что считается известным, а именно школьную арифметику, привычку следовать письменным инструкциям и умение обращаться с файлами и папками; формулирует задачу, которую решает раздел, то есть как превратить вычисление, которое умеешь выполнять вручную, в процедуру, которую машина выполняет без ошибок и без усталости; и показывает, куда раздел ведёт: к следующим разделам и к курсу «Программирование на C++ II».

Цели обучения

  1. Описывать последовательность содержания раздела и объяснять, что каждый урок даёт уроку, который идёт за ним.
  2. Распознавать предварительные знания, которые предполагает курс (действия с целыми и десятичными числами, вычисление частного и остатка, выполнение письменных инструкций, сохранение файла в заданной папке), и при необходимости повторять их.
  3. Объяснять на примере путь, который раздел учит проходить: от формулировки к спецификации, от спецификации к алгоритму и от алгоритма к программе, которая компилируется и запускается.
  4. Понимать, что ожидается уметь к концу раздела, и отличать это от того, что будет изучено в следующих разделах.

Входная проверка

Прежде чем проходить раздел, полезно определить исходный уровень. Эта проверка является самопроверкой: она не оценивается, и её единственная цель состоит в том, чтобы направить занятия. Поскольку это первый раздел курса, вопросов о предыдущих разделах нет; вместо них первые три вопроса касаются того, что курс считает известным: частного и остатка при целочисленном делении, порядка шагов процедуры, а также имени, расширения и папки файла. Следующие семь по порядку касаются центральных идей шести уроков раздела. Тот, кто никогда не программировал, не должен рассчитывать ответить на них верно, и это не важно: каждый ответ, верный или нет, объясняет суть дела и называет урок, где она разбирается. Те же умения измеряются снова в конце, в уроке итогов.

Входная проверка

Перед началом раздела оцени свою отправную точку. Это короткая самопроверка: она не оценивается, а результат хранится только в твоём браузере.

  1. Вопрос 1Из предыдущих разделовРаздают 47 листов 6 людям поровну, целыми частями, а те листы, которых не хватает на полный круг раздачи, откладывают. Сколько листов получает каждый и сколько откладывают?

  2. Вопрос 2Из предыдущих разделовЧтобы отправить файл вложением по электронной почте, нужно сделать четыре шага: A, написать адрес получателя; B, нажать «Отправить»; C, открыть новое письмо; D, прикрепить файл. Какой порядок правильный?

  3. Вопрос 3Из предыдущих разделовФайл сохранён по пути C:\Users\ana\programas\suma.cpp. Каково его имя, каково расширение и в какой папке он находится?

  4. Вопрос 4В рецепте сказано: «Если тесто сухое, добавить ложку воды и вымесить; повторять, пока оно не перестанет быть сухим». Тесту нужно ровно три ложки, чтобы перестать быть сухим. Сколько раз задаётся вопрос, сухое ли тесто?

  5. Вопрос 5Какую из этих инструкций любой человек или машина могут выполнить совершенно одинаково?

  6. Вопрос 6Программа компилируется без ошибок и предупреждений, правильно работает на многих данных, а на одном конкретном значении останавливается с сообщением «division by zero». В какой момент возникла ошибка?

  7. Вопрос 7Установлен Visual Studio Code с расширением для C++, а когда в терминале запрашивают g++ --version, терминал отвечает, что не распознаёт g++. Что наиболее вероятно?

  8. Вопрос 8Компилятор выводит сообщение, которое начинается с warning:, и, несмотря на него, создаёт исполняемый файл. Что это значит?

  9. Вопрос 9С чего начинается выполнение программы на C++?

  10. Вопрос 10Программа объявляет int age{};, выполняет std::cin >> age;, чтобы считать возраст, а пользователь вводит abc. Что происходит?

Содержание раздела

В разделе шесть уроков, которые по порядку проходят путь от задачи до работающей программы. Каждый предполагает тот, что идёт перед ним, а первый не предполагает ничего из языка.

  • От задачи к алгоритму. Курс начинается без кода, потому что алгоритм предшествует программе. Урок учит специфицировать задачу через её вход, её выход и связь между ними (предусловие и постусловие); описывать алгоритм пронумерованными шагами, без двусмысленности; выполнять его вручную с помощью таблицы трассировки; распознавать в нём следование, ветвление и повторение; и обосновывать, что повторение завершается, с помощью величины, которая убывает на каждой итерации.
  • Программы, компиляторы и C++. Алгоритм, записанный для машины, есть программа, а программа на C++ не выполняется в том виде, в каком написана: препроцессор, компилятор и компоновщик превращают её в исполняемый файл. Урок проходит эти три этапа, отличает то, что обнаруживается при компиляции, от того, что проявляется только при выполнении, сравнивает модель компиляции с моделью интерпретации и располагает во времени стандарты языка вплоть до C++23, который использует курс, и C++26, реализация которого в компиляторах ещё продолжается.
  • Установка среды в Windows, macOS и Linux. Три полных руководства, по одному на систему, чтобы установить компилятор с поддержкой C++23 и редактор Visual Studio Code с расширением для C++, убедиться с помощью проверочной программы, что всё работает, и устранить частые неполадки: компилятор, которого не находит терминал, старую версию и стандарт, который не был запрошен.
  • Компиляция, запуск и настройка. Рабочий цикл правки, сохранения, компиляции, запуска и чтения из терминала и из редактора, с командой компиляции курса. Урок отличает ошибки, которые не дают получить исполняемый файл, от предупреждений, которые указывают на подозрительный код; включает санитайзеры (санитайзер ошибок памяти, AddressSanitizer, и санитайзер неопределённого поведения, UndefinedBehaviorSanitizer), которые следят за программой во время её работы; и объясняет, почему в выводе появляются странные символы вместо букв кириллицы.
  • Анатомия программы. Минимальная программа по частям: директива включения и заголовочные файлы, функция main и код возврата, пространство имён std, инструкции, блоки и комментарии. Урок учит исправлять ошибки структуры, читая сообщения компилятора начиная с первого, и устанавливает руководство по стилю курса.
  • Ввод, вывод и ошибки. Программа общается с внешним миром: выводит текст функциями std::print и std::println, со спецификациями формата для ширины и числа знаков после точки, и считывает данные через std::cin. Урок показывает, что происходит, когда ввод не имеет ожидаемой формы, и как проверяется чтение, и завершает раздел классификацией ошибок по моменту их появления: ошибки компиляции, компоновки, времени выполнения и логические ошибки.

Связывает их нить самой программы. Первый урок её задумывает, второй объясняет, во что она превращается, третий устанавливает инструменты, которые её превращают, четвёртый учит ими пользоваться и слушать, что они говорят, пятый её препарирует, а шестой заставляет её разговаривать с тем, кто ею пользуется. Если это так, то в конце раздела студент знает о C++ ещё очень мало, но знает, как с ним работать, и именно это предполагает остальная часть курса.

Что считается известным

Курс не предполагает никакого опыта программирования. Зато он предполагает три умения, которые стоит повторить сейчас, если входная проверка выявила какие-либо сомнения.

Первое есть школьная арифметика: действия с целыми и десятичными числами, соблюдение порядка действий (умножение и деление раньше сложения и вычитания, если нет скобок) и прежде всего вычисление частного и остатка при целочисленном делении. Последнее важнее, чем кажется. Если разделить 47 листов между 6 людьми, каждый получит 7 и останется 5, что записывается как 47=6⋅7+547 = 6 \cdot 7 + 5, причём остаток 55 меньше делителя 66. В C++ есть оператор для частного и другой для остатка, и значительная часть вычислений с целыми числами (перевод секунд в часы, разделение числа на цифры, выяснение того, чётно ли число) сводится к ним. Кто хочет изучить целочисленное деление подробно, найдёт его вместе с доказательством в курсе «Алгебра и геометрия I: числа, функции и геометрия плоскости»; здесь достаточно уметь его выполнять.

Второе есть привычка следовать письменным инструкциям буквально и замечать, когда инструкции недостаточно, чтобы понять, что делать. Речь идёт не о знании, а об установке: прочитать «добавить соль по вкусу» и заметить, что эта инструкция зависит от того, кто её выполняет. Первый урок раздела превращает эту установку в метод.

Третье есть основы обращения с компьютером: создать папку, сохранить в ней файл под выбранным именем, распознать расширение имени (.cpp, .txt, .exe) и знать, в какой папке находится каждый файл. Стоит уже сейчас настроить систему так, чтобы она показывала расширения, которые некоторые системы, например Windows, по умолчанию скрывают: исходный код suma.cpp и исполняемый файл suma.exe совсем разные файлы, и их путаница становится источником нелепых ошибок. Раздел учит также пользоваться терминалом, так что заранее уметь это не нужно; достаточно быть готовым набирать команды вместо того, чтобы нажимать кнопки. Наконец, чтобы устанавливать программы, нужно иметь такую возможность на собственном компьютере; урок об установке указывает онлайн-альтернативу для тех, у кого её нет.

Английский язык не предполагается. Сообщения компилятора написаны по-английски, и курс учит читать их поле за полем, так что то, что в них важно (файл, строка, вид дефекта), понятно и без владения языком. Не предполагается и никакой математики сверх школьной: в примерах используются деление, средние значения, таблица умножения, даты и время.

Задача, которую решает раздел

Вычисление, которое умеешь выполнять вручную, ещё не есть программа. Расстояние между ними раздел и учит преодолевать, а простая задача позволяет его измерить.

ПримерДлительность в часах, минутах и секундах

Секундомер записывает длительность каждого действия как число секунд, например 7384, и её нужно записать в привычной форме часов, минут и секунд, где минуты и секунды всегда занимают две цифры: 2:03:04. Вручную это вычисление есть цепочка делений: 7384=3600⋅2+1847384 = 3600 \cdot 2 + 184, откуда получаются 2 часа и 184 секунды; 184=60⋅3+4184 = 60 \cdot 3 + 4, откуда получаются 3 минуты и 4 секунды. Для одной длительности это делается за минуту. Для тысячи длительностей из журнала, напротив, вычисление вручную долгое и чревато ошибками, и такую работу естественно поручить машине.

Однако машина не может получить приведённое выше объяснение. Она получает текст на языке программирования, а чтобы написать этот текст, нужно сначала решить три вопроса, которые вычисление вручную оставляло неявными. Первый есть спецификация: что поступает на вход (целое число секунд, неотрицательное), что выдаётся на выходе (три целых числа: часы, минуты и секунды) и какое соотношение их связывает: если tt есть общее число секунд, то t=3600h+60m+st = 3600 h + 60 m + s, где 0≤m<600 \leq m < 60 и 0≤s<600 \leq s < 60. Второй есть алгоритм: точные шаги, которые для любого допустимого входа дают этот выход.

  1. hh принимает значение частного от деления tt на 36003600.
  2. mm принимает значение частного от деления на 6060 остатка от деления tt на 36003600.
  3. ss принимает значение остатка от деления tt на 6060.
  4. Записать hh, двоеточие, mm двумя цифрами, двоеточие и ss двумя цифрами.

Третий есть запись алгоритма на C++ в точной форме, которой требует язык, и его перевод в исполняемый файл с помощью подходящих инструментов. Это делает программа ниже. В ней много такого, что ещё не объяснялось: переменные (урок 2.1), операторы / и %, которые дают частное и остаток при целочисленном делении (урок 3.1), чтение через std::cin и спецификации формата (урок 1.6); спецификация {:02} в этой программе, которая записывает число в две позиции, дополняя его нулями слева, есть вариант ширины, которому учит тот урок и которым воспользуется урок 3.1. Пока достаточно узнать в ней четыре шага алгоритма.

Программа на C++
#include <iostream>#include <print>​int main(){    int total{};  // переменные будут объяснены в уроке 2.1    std::print("Длительность в секундах: ");    std::cin >> total;    const int hours{total / 3600};    const int minutes{total % 3600 / 60};    const int seconds{total % 60};    std::println("{} s = {}:{:02}:{:02}", total, hours, minutes, seconds);}
Ввод программы
7384
Вывод
Длительность в секундах: 7384 s = 2:03:04

Строки с 9 по 11 соответствуют шагам с 1 по 3, а строка 12 соответствует шагу 4. То, что результат совпадает с вычисленным вручную, не случайность, а испытание, которого заслуживает каждая программа: сначала опробовать её на данных, ответ для которых известен.

Программа решает задачу, но раздел учит также ей не доверять. Если пользователь введёт abc вместо числа, чтение потерпит неудачу, переменная total останется равной нулю, и программа без всякого сигнала тревоги выведет, что длительность равна 0:00:00: ложное заключение, которое выглядит истинным. Если он введёт отрицательную длительность, спецификация не говорит, что должно произойти, и результат лишён смысла. Если написать total / 360 вместо total / 3600, программа компилируется, запускается и даёт неверный результат, и ни один инструмент об этом не предупреждает. Каждый из этих дефектов относится к своему виду ошибок, который обнаруживается в свой момент и ищется своим способом; последний урок раздела их классифицирует. Отсюда следует, что задача, которую решает раздел, состоит не только в том, чтобы написать эту программу, но и в том, чтобы знать, почему она работает, на каких данных перестаёт работать и какой инструмент предупредит, или не предупредит, о каждом дефекте.

Следовательно, к концу раздела студент сможет писать, компилировать и запускать программы такого рода: несколько входных данных, несколько вычислений по порядку и несколько результатов, выведенных с форматированием. Он ещё не сможет принимать решения и повторять инструкции на C++ (хотя сможет в алгоритме, записанном по-русски), а также осознанно выбирать тип каждого значения; это принесут следующие разделы.

Куда он ведёт

Раздел есть порог курса. То, что в нём используется без объяснений, объясняется позже, в таком порядке. Раздел 2, «Данные и типы», объясняет переменные и то, как они хранятся в памяти, пределы целых чисел (и почему правильное вычисление может вызвать переполнение), округление чисел с дробной частью и преобразования между типами. Раздел 3, «Выражения и операторы», объясняет арифметику C++ с частным и остатком для отрицательных чисел, логику условий и то, что стандарт оставляет неопределённым в выражении. Раздел 4, «Управление потоком», переносит в C++ ветвления и повторения, которые первый урок записывает по-русски, и учит обосновывать, что цикл корректен и завершается. Раздел 5 разбивает программы на функции; раздел 6 вводит составные данные (массивы, векторы, таблицы и записи) с алгоритмами поиска и сортировки и их сложностью; раздел 7 посвящён тексту, надёжному чтению ввода и файлам; а раздел 8 рассматривает программы из нескольких файлов, систематическое тестирование и отладчик.

После этого курса «Программирование на C++ II» продолжит то, что этот курс намеренно оставляет за рамками: указатели и динамическую память, классы с методами, собственные шаблоны, анонимные функции и исключения. Ни одна из этих тем не нужна для решения задач этого курса, а их откладывание позволяет сосредоточиться на том, что верно для любого языка: специфицировать, проектировать алгоритм, ясно его записывать и проверять, что он корректен. Урок итогов этого раздела соберёт именно это различие между тем, что свойственно C++, и тем, что перейдёт в любой другой язык.