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

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

До сих пор каждая программа курса была одним блоком, main, в котором всё пишется там, где используется. Этот урок учит давать имя части программы и пользоваться ею, не заглядывая внутрь: это функция. Изучаются объявление, которое сообщает компилятору имя, тип возвращаемого значения и параметры; определение, которое добавляет тело; и вызов, который передаёт управление этому телу и получает возвращённое значение. Различается то, что компилятор проверяет при компиляции (что каждый вызов согласуется с объявлением), и то, что происходит при выполнении (переход к определению и возврат). Объясняется, почему достижение конца функции, которая должна вернуть значение, без return является неопределённым поведением. Выполнение прослеживается по стеку вызовов, который хранит по одному кадру на каждый незавершённый вызов. И каждая функция специфицируется контрактом, её предусловием и постусловием, который говорит, чего функция требует от вызывающего и что гарантирует взамен.

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

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

Зачем нужны функции

Программы предыдущего раздела решали каждую задачу внутри main, и этого хватало, пока задачи были небольшими. Однако, как только программа растёт, возникают три трудности. Первая из них связана с повторением: если одно и то же вычисление нужно в трёх местах, оно пишется трижды, и ошибка в нём исправляется трижды (или в одном из мест её забывают исправить). Вторая касается чтения: блок из пятидесяти строк говорит не о том, что он делает, а о том, как он это делает, и читатель должен восстанавливать замысел по механизму. Третья касается тестирования: если всё перемешано, невозможно испытать одну часть, не выполняя остальные.

Функция есть именованный фрагмент программы, который получает данные, называемые параметрами, и выдаёт результат, называемый возвращаемым значением. Математика знает эту идею уже много веков: расстояние между двумя точками плоскости есть функция их четырёх координат, d(x1,y1,x2,y2)=(x2−x1)2+(y2−y1)2d(x_1, y_1, x_2, y_2) = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2}, и тот, кто вычисляет периметр треугольника, не повторяет формулу трижды, а даёт ей имя и применяет её трижды. В C++ пишут то же самое.

ПримерПериметр треугольника

Программа читает координаты трёх вершин треугольника и выводит его периметр, то есть сумму длин трёх его сторон.

Программа на C++
#include <cmath>#include <iostream>#include <print>​// Расстояние между точками (x1, y1) и (x2, y2).double distance(double x1, double y1, double x2, double y2){    const double dx{x2 - x1};    const double dy{y2 - y1};    return std::sqrt(dx * dx + dy * dy);}​int main(){    double ax{};    double ay{};    double bx{};    double by{};    double cx{};    double cy{};    std::cin >> ax >> ay >> bx >> by >> cx >> cy;    const double perimeter{distance(ax, ay, bx, by) + distance(bx, by, cx, cy)                           + distance(cx, cy, ax, ay)};    std::println("Периметр: {}", perimeter);}
Ввод программы
0 0 3 0 0 4
Вывод
Периметр: 12

Стороны треугольника с вершинами (0,0)(0, 0), (3,0)(3, 0) и (0,4)(0, 4) равны 3, 5 и 4, а их сумма равна 12.

Функция distance пишется один раз и используется трижды; main читается как определение периметра, «сумма трёх расстояний»; а distance можно протестировать отдельно, на паре точек с известным расстоянием. Тем самым получены все три вещи, которых недоставало. Общий принцип, верный для любого языка, состоит в том, что функция даёт имя идее: тот, кто ею пользуется, думает о том, что она делает, а не о том, как. Такое разделение использования и реализации называется процедурной абстракцией, и оно составляет тему всего раздела.