Краткое содержание
Этот урок открывает первый раздел курса, посвящённый первым шагам в программировании на C++. Он объясняет, для чего нужен раздел: провести того, кто никогда не программировал, от задачи, сформулированной обычным языком, до программы, которая компилируется, запускается, считывает данные и выводит результаты, причём так, чтобы было понятно, что сделал на этом пути каждый инструмент; представляет шесть уроков раздела и нить, которая их связывает, от алгоритма, записанного по-русски, до классификации ошибок; уточняет то немногое, что считается известным, а именно школьную арифметику, привычку следовать письменным инструкциям и умение обращаться с файлами и папками; формулирует задачу, которую решает раздел, то есть как превратить вычисление, которое умеешь выполнять вручную, в процедуру, которую машина выполняет без ошибок и без усталости; и показывает, куда раздел ведёт: к следующим разделам и к курсу «Программирование на C++ II».
Цели обучения
- Описывать последовательность содержания раздела и объяснять, что каждый урок даёт уроку, который идёт за ним.
- Распознавать предварительные знания, которые предполагает курс (действия с целыми и десятичными числами, вычисление частного и остатка, выполнение письменных инструкций, сохранение файла в заданной папке), и при необходимости повторять их.
- Объяснять на примере путь, который раздел учит проходить: от формулировки к спецификации, от спецификации к алгоритму и от алгоритма к программе, которая компилируется и запускается.
- Понимать, что ожидается уметь к концу раздела, и отличать это от того, что будет изучено в следующих разделах.
Входная проверка
Прежде чем проходить раздел, полезно определить исходный уровень. Эта проверка является самопроверкой: она не оценивается, и её единственная цель состоит в том, чтобы направить занятия. Поскольку это первый раздел курса, вопросов о предыдущих разделах нет; вместо них первые три вопроса касаются того, что курс считает известным: частного и остатка при целочисленном делении, порядка шагов процедуры, а также имени, расширения и папки файла. Следующие семь по порядку касаются центральных идей шести уроков раздела. Тот, кто никогда не программировал, не должен рассчитывать ответить на них верно, и это не важно: каждый ответ, верный или нет, объясняет суть дела и называет урок, где она разбирается. Те же умения измеряются снова в конце, в уроке итогов.
Перед началом раздела оцени свою отправную точку. Это короткая самопроверка: она не оценивается, а результат хранится только в твоём браузере.
Вопрос 1Из предыдущих разделовРаздают 47 листов 6 людям поровну, целыми частями, а те листы, которых не хватает на полный круг раздачи, откладывают. Сколько листов получает каждый и сколько откладывают?
Это деление с дробной частью, а не целочисленное: листы не режут, а вопрос спрашивает, сколько остаётся.Округление частного вверх требует листов, которых нет; остаток целочисленного деления никогда не бывает отрицательным, когда делимое и делитель положительны.Смешивает остаток с дробной частью частного: остаток считается в листах, а не в долях круга.Это целочисленное деление: частное есть наибольшее число полных кругов, а остаток есть то, что остаётся, и он всегда меньше делителя. Курс пользуется им с первой программы, и в C++ у него есть собственные операторы.Повторить: От задачи к алгоритму
Вопрос 2Из предыдущих разделовЧтобы отправить файл вложением по электронной почте, нужно сделать четыре шага: A, написать адрес получателя; B, нажать «Отправить»; C, открыть новое письмо; D, прикрепить файл. Какой порядок правильный?
Порядок перечисления не есть порядок выполнения: при нём письмо было бы отправлено раньше, чем появилось.Результат зависит от порядка: то, что прикреплено после отправки, не дойдёт.Письмо уходит без получателя и без вложения; последующие шаги его уже не меняют.Одни шаги зависят от других (нельзя прикрепить файл к письму, которое не открыто, или заполнить уже отправленное), а другие нет. Распознать эти зависимости есть первое, чего требует запись алгоритма.Повторить: От задачи к алгоритму
Вопрос 3Из предыдущих разделовФайл сохранён по пути
C:\Users\ana\programas\suma.cpp. Каково его имя, каково расширение и в какой папке он находится?Смешивает папку, в которой лежит файл, с самим файлом: имя есть последний компонент пути.Расширение есть часть имени; некоторые системы по умолчанию его скрывают, а курс просит его показывать, потому чтоsuma.cppиsuma.exeсовсем разные файлы.C:есть диск, с которого начинается путь, а не расширение.Путь перечисляет папки от корня до файла; расширение, то есть то, что идёт после последней точки в имени, говорит, какого рода содержимое в файле:.cppозначает исходный код на C++. Чтобы его скомпилировать, терминал должен находиться в этой папке или получить полный путь.Повторить: Компиляция, запуск и настройка
Вопрос 4В рецепте сказано: «Если тесто сухое, добавить ложку воды и вымесить; повторять, пока оно не перестанет быть сухим». Тесту нужно ровно три ложки, чтобы перестать быть сухим. Сколько раз задаётся вопрос, сухое ли тесто?
Не учитывает вопрос с ответом «нет», который тоже задаётся: без него никто не узнал бы, что пора перестать добавлять воду.Смешивает текст процедуры с её выполнением: условие записано один раз, но вычисляется на каждой итерации.Считает промежутки между ложками, а не вопросы.В любом повторении, которое спрашивает прежде, чем действовать, условие вычисляется на один раз больше, чем выполняется тело: именно последний вопрос позволяет выйти. Если пройти процедуру вручную, считая, это видно без всякой двусмысленности.Повторить: От задачи к алгоритму
Вопрос 5Какую из этих инструкций любой человек или машина могут выполнить совершенно одинаково?
Зависит от вкуса того, кто выполняет: два человека выполнили бы её по-разному, а у машины нет вкуса.Не сказано, сколько раз и что значит «получится хорошо»: условие завершения нельзя проверить.Числа не заданы: «большое» не есть количество, а делитель может оказаться нулём.Она говорит, какая операция выполняется, с какими данными и где остаётся результат; ничто не зависит от суждения того, кто её выполняет. Эта определённость есть одно из трёх требований к алгоритму.Повторить: От задачи к алгоритму
Вопрос 6Программа компилируется без ошибок и предупреждений, правильно работает на многих данных, а на одном конкретном значении останавливается с сообщением «division by zero». В какой момент возникла ошибка?
Компилятор переводит текст, а не выполняет его, и не знает данных, которые введёт пользователь.Компоновщик собирает части программы; он не выполняет вычислений и не видит данных.С другими данными тот же исполняемый файл работает: дефект в том, что программа делает с этими данными, а не в инструментах.То, что зависит только от текста программы, обнаруживается при компиляции или компоновке; то, что зависит от данных, обнаруживается только при выполнении. Поэтому то, что программа компилируется, само по себе ещё не значит, что в ней нет ошибок.Повторить: Программы, компиляторы и C++
Вопрос 7Установлен Visual Studio Code с расширением для C++, а когда в терминале запрашивают
g++ --version, терминал отвечает, что не распознаётg++. Что наиболее вероятно?Расширение не содержит компилятора и не влияет на то, что находит терминал.Командаg++ --versionне компилирует никакой программы: она только спрашивает версию, а терминал даже не нашёл, у кого её спросить.Редактор не важен: недостаёт компилятора, в каком бы редакторе ни работать.Редактор не компилирует: он вызывает отдельный компилятор, который должен быть установлен и доступен терминалу. Расширение для C++ помогает писать код, но компилятора не содержит.Повторить: Установка среды в Windows, macOS и Linux
Вопрос 8Компилятор выводит сообщение, которое начинается с
warning:, и, несмотря на него, создаёт исполняемый файл. Что это значит?Это была бы ошибка (error:), и тогда исполняемого файла не было бы.То, что программа работает на опробованных данных, ничего не доказывает об остальных; проигнорированное предупреждение часто оказывается завтрашней ошибкой.Исполняемый файл обычный; сомнительно то, что программа делает в указанном месте.Ошибка не даёт получить исполняемый файл; предупреждение позволяет его получить, но указывает на подозрительный код. Многие предупреждения выдают настоящие дефекты, и поэтому курс требует программ, которые компилируются без единого предупреждения.Повторить: Компиляция, запуск и настройка
Вопрос 9С чего начинается выполнение программы на C++?
Первая строка обычно содержит директиву#include, которая обрабатывается до компиляции и не выполняется.Точку входа определяет не порядок в файле, а имяmain.Имя точки входа фиксирует стандарт; его не выбирают в команде компиляции.Функцияmainесть точка входа: система вызывает её при запуске программы, и программа завершается, когда завершается она. Программа безmainкомпилируется, но не компонуется.Повторить: Анатомия программы
Вопрос 10Программа объявляет
int age{};, выполняетstd::cin >> age;, чтобы считать возраст, а пользователь вводитabc. Что происходит?Она не останавливается: она продолжает работу так, будто чтение прошло успешно, и поэтому дефект опасен.Переменная типаintхранит только целые числа; буквы не превращаются в число и не сохраняются.Чтение не повторяется: оно один раз терпит неудачу и оставляет поток в состоянии ошибки.Считывание целого числа терпит неудачу, встретив букву; стандарт предписывает, что переменная получает 0 и что последующие операции чтения ничего не будут делать, пока ошибка не будет обработана. Если программа не проверяет чтение, она без всякого сигнала пишет ложные заключения.Повторить: Ввод, вывод и ошибки
Содержание раздела
В разделе шесть уроков, которые по порядку проходят путь от задачи до работающей программы. Каждый предполагает тот, что идёт перед ним, а первый не предполагает ничего из языка.
- От задачи к алгоритму. Курс начинается без кода, потому что алгоритм предшествует программе. Урок учит специфицировать задачу через её вход, её выход и связь между ними (предусловие и постусловие); описывать алгоритм пронумерованными шагами, без двусмысленности; выполнять его вручную с помощью таблицы трассировки; распознавать в нём следование, ветвление и повторение; и обосновывать, что повторение завершается, с помощью величины, которая убывает на каждой итерации.
- Программы, компиляторы и 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, что записывается как , причём остаток меньше делителя . В C++ есть оператор для частного и другой для остатка, и значительная часть вычислений с целыми числами (перевод секунд в часы, разделение числа на цифры, выяснение того, чётно ли число) сводится к ним. Кто хочет изучить целочисленное деление подробно, найдёт его вместе с доказательством в курсе «Алгебра и геометрия I: числа, функции и геометрия плоскости»; здесь достаточно уметь его выполнять.
Второе есть привычка следовать письменным инструкциям буквально и замечать, когда инструкции недостаточно, чтобы понять, что делать. Речь идёт не о знании, а об установке: прочитать «добавить соль по вкусу» и заметить, что эта инструкция зависит от того, кто её выполняет. Первый урок раздела превращает эту установку в метод.
Третье есть основы обращения с компьютером: создать папку, сохранить в ней файл под выбранным именем, распознать расширение имени (.cpp, .txt, .exe) и знать, в какой папке находится каждый файл. Стоит уже сейчас настроить систему так, чтобы она показывала расширения, которые некоторые системы, например Windows, по умолчанию скрывают: исходный код suma.cpp и исполняемый файл suma.exe совсем разные файлы, и их путаница становится источником нелепых ошибок. Раздел учит также пользоваться терминалом, так что заранее уметь это не нужно; достаточно быть готовым набирать команды вместо того, чтобы нажимать кнопки. Наконец, чтобы устанавливать программы, нужно иметь такую возможность на собственном компьютере; урок об установке указывает онлайн-альтернативу для тех, у кого её нет.
Английский язык не предполагается. Сообщения компилятора написаны по-английски, и курс учит читать их поле за полем, так что то, что в них важно (файл, строка, вид дефекта), понятно и без владения языком. Не предполагается и никакой математики сверх школьной: в примерах используются деление, средние значения, таблица умножения, даты и время.
Задача, которую решает раздел
Вычисление, которое умеешь выполнять вручную, ещё не есть программа. Расстояние между ними раздел и учит преодолевать, а простая задача позволяет его измерить.
Секундомер записывает длительность каждого действия как число секунд, например 7384, и её нужно записать в привычной форме часов, минут и секунд, где минуты и секунды всегда занимают две цифры: 2:03:04. Вручную это вычисление есть цепочка делений: , откуда получаются 2 часа и 184 секунды; , откуда получаются 3 минуты и 4 секунды. Для одной длительности это делается за минуту. Для тысячи длительностей из журнала, напротив, вычисление вручную долгое и чревато ошибками, и такую работу естественно поручить машине.
Однако машина не может получить приведённое выше объяснение. Она получает текст на языке программирования, а чтобы написать этот текст, нужно сначала решить три вопроса, которые вычисление вручную оставляло неявными. Первый есть спецификация: что поступает на вход (целое число секунд, неотрицательное), что выдаётся на выходе (три целых числа: часы, минуты и секунды) и какое соотношение их связывает: если есть общее число секунд, то , где и . Второй есть алгоритм: точные шаги, которые для любого допустимого входа дают этот выход.
- принимает значение частного от деления на .
- принимает значение частного от деления на остатка от деления на .
- принимает значение остатка от деления на .
- Записать , двоеточие, двумя цифрами, двоеточие и двумя цифрами.
Третий есть запись алгоритма на C++ в точной форме, которой требует язык, и его перевод в исполняемый файл с помощью подходящих инструментов. Это делает программа ниже. В ней много такого, что ещё не объяснялось: переменные (урок 2.1), операторы / и %, которые дают частное и остаток при целочисленном делении (урок 3.1), чтение через std::cin и спецификации формата (урок 1.6); спецификация {:02} в этой программе, которая записывает число в две позиции, дополняя его нулями слева, есть вариант ширины, которому учит тот урок и которым воспользуется урок 3.1. Пока достаточно узнать в ней четыре шага алгоритма.
#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++, и тем, что перейдёт в любой другой язык.