Функции - Язык программирования C++ для спортивного программирования
открываем урок…
Неделя 7Функции, Параметры и Область видимости, Декомпозиция программы и Отладка
Функции
Как написать и вызвать функцию, параметры и аргументы, что происходит при вызове, return и ранний выход, функции типа bool и void и зачем программы собирают из функций.
Теория
Урок 1 из 9
15 мин чтения
Вопросов: 5
дальше
Практика: Функции
Семь задач, в каждой — одна небольшая функция: модуль числа, чётность, ограничение диапазоном, високосные годы, треугольники, перевёрнутые числа и степени.
Вы вызываете функции с первой недели: main — функция, и max, min, abs
и sqrt — тоже. Вы даёте max(4, 9) два числа, она делает свою работу, и вы
получаете обратно одно число — не зная и не задумываясь, как она устроена внутри.
На этой неделе вы научитесь писать свои. Функция — это кусок кода с именем: его пишут
один раз, а потом вызывают по имени сколько угодно раз. Программа перестаёт
быть одной длинной main и превращается в несколько небольших частей, у каждой
из которых одно дело.
Ваша первая функция
Вот функция, которая возводит число в квадрат:
Часть
Что означает
int (перед именем)
тип возвращаемого значения: функция отдаёт обратно int
square
имя функции
(int x)
параметр: переменная, которая получает значение, переданное при вызове
{ … }
тело: код, который выполняется при каждом вызове
return x * x;
завершает вызов и отдаёт значение обратно
Само определение функции ничего не выполняет. Её тело выполняется, когда кто-то её
вызывает:
В square(7) значение 7 — это аргумент. При вызове создаётся параметр x
и получает значение 7; тело вычисляет 49; return отдаёт 49 туда, откуда
функцию вызвали, и square(7) в этом месте просто превращается в 49.
Параметры — это переменные в определении функции; аргументы — значения в
конкретном вызове.
Где её писать. C++ читает файл сверху вниз, и функция должна быть известна
раньше строки, которая её вызывает. Пишите функции над main. Вот целая
программа, написанная так, для такой задачи:
Даны три целых числа. Выведите наибольшее из них.
У bigger два параметра, через запятую. Параметров у функции может быть столько,
сколько ей нужно, и каждый вызов должен передать ровно столько же аргументов, в
том же порядке. Для 4 17 9 программа печатает 17.
Что происходит при вызове
Когда main доходит до вызова, она останавливается. Функция начинает работу
с совершенно новыми параметрами, в которые записаны значения аргументов, и
выполняется до return. Затем её переменные исчезают, а main продолжает с того
места, где остановилась, — и на месте вызова стоит возвращённое значение.
Повторный вызов функции начинается с чистого листа: новые параметры, и ничего
не осталось с прошлого раза.
Пройдите по шагам bigger(bigger(3, 9), 5) и следите за стеком вызовов — это
список вызовов, которые уже начались, но ещё не закончились. Сначала выполняется
внутренний вызов, и его ответ становится аргументом внешнего.
bigger(bigger(3, 9), 5): сначала внутренний вызов
C++
1#include <bits/stdc++.h>
2using namespace std;
3
4int bigger(int a, int b) {
5 if (a > b) {
6 return a;
7 }
8 return b;
9}
10
11int main() {
12 ios_base::sync_with_stdio(false);
13 cin.tie(NULL);
14
15 cout << bigger(bigger(3, 9), 5) << "\n";
16 return 0;
17}
Начинаем в main()
строка 11
Программа начинается в main(). Функция bigger только определена выше — в ней пока ничего не выполнялось.
стек вызовов
main()
a—b—
нажми пуск
1 / 9
return
return завершает функцию сразу: всё, что после него, не выполняется. В
функции может быть несколько return, и срабатывает первый, до которого дошло
дело. Поэтому особые случаи удобно разбирать в самом начале:
Тип возвращаемого значения может быть любым из известных вам типов. Функция,
которая отвечает на вопрос «да или нет», возвращает bool, и её вызов можно
сразу поставить в if:
Когда значения могут быть большими, возвращайте long long и принимайте
параметры типа long long — типы работают ровно так же, как у переменных.
Функция, которая возвращает int, обрезает свой ответ до int, какими бы
большими ни были числа внутри неё.
Каждый путь выполнения функции, у которой есть тип возвращаемого значения,
должен дойти до return. Если какой-то путь до него не доходит, компилятор только
предупреждает, программа всё равно запускается, и такой вызов возвращает мусор —
или программа падает. Не выключайте предупреждения и читайте их.
void: функции, которые только что-то делают
Некоторые функции ничего не вычисляют — они только что-то печатают. Их тип
возвращаемого значения — void, «ничего». Вот программа с такой функцией:
Дано число n. Нарисуйте из знаков # лесенку высотой n строк, которая
поднимается слева направо.
Для 4 программа печатает:
Функцию типа void вызывают отдельной инструкцией, например printRow(i, '#');.
Значения у неё нет, поэтому int k = printRow(3, '#'); не компилируется. Внутри
неё return; без значения досрочно завершает вызов.
Теперь побудьте компьютером. Каждая машина ниже — это функция: положите значение
внутрь и скажите, что выйдет, раньше, чем это сделает машина.
Машина-функцияпопробуй!
Функция с циклом внутри
В теле функции может быть всё то же, что и в main: переменные, if, циклы.
Подсчёт цифр числа — цикл, который вы уже знаете, а в виде функции он получает
имя, и его можно использовать где угодно:
В первой строке дано q, а в следующей — q целых чисел от 0 до 10¹⁸. Для
каждого числа выведите, сколько в нём цифр.
Для q = 4 и чисел 7 2026 0 100000 программа печатает 1 4 1 6. Обратите внимание на
две вещи. Для digitCount(0) нужен свой return 1 — цикл не насчитал бы ни
одной цифры. И цикл меняет n, но это n принадлежит функции: x в main
остаётся нетронутым. Почему так — тема следующего урока.
Функции могут вызывать другие функции. Когда есть bigger, наибольшее из трёх
чисел — это одна строка:
Зачем всё это?
Проверьте себя
Что даёт функция
Имя для идеиif (isVowel(c)) говорит, что проверяется. Пять сравнений через || говорят, как, — а зачем, читателю приходится догадываться.
Написать один раз, использовать много раз Одна и та же проверка в трёх местах — это три шанса ошибиться. В функции её пишут и исправляют один раз.
Проверять по частям Маленькую функцию можно испытать отдельно: правда ли digitCount(0) даёт 1? Проверить это проще, чем всю программу целиком.
Короткая mainmain читает ввод, вызывает помощников и печатает — весь план виден с одного взгляда.
А вы знали?
main — тоже функция, и её return 0; уходит операционной системе: 0 означает
«всё прошло хорошо», а любое другое число — что-то сломалось. Проверяющие системы
на это смотрят. Если программа падает или возвращает не 0, вердикт будет
Runtime error (ошибка во время выполнения), даже если всё напечатанное было
верным.
int square(int x){ return x * x;}
int a = square(7); // a = 49cout << square(3) + square(4)<< "\n"; // 9 + 16, печатает 25
проверь себя1 балл
Что напечатает программа?
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
Чему равно g(12) + g(7) + g(5)?
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
printRow — функция типа void. Строка int k = printRow(3, '#'); скомпилируется.
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
bigger(a, b) возвращает большее из двух чисел. Сколько раз вызывается bigger, пока выполняется эта строка?
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
Функция — int square(int x) { return x * x; }, а main вызывает square(7). Что здесь параметр, а что аргумент?
Выберите один ответ
Войдите, чтобы ответить и сохранить прогресс.Войти
#
##
###
####
#include <bits/stdc++.h>using namespace std;int bigger(int a, int b){ // получает два числа, возвращает одно if(a > b){ return a;} return b;}int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); int x, y, z; cin >> x >> y >> z; cout << bigger(bigger(x, y), z)<< "\n"; // сначала выполняется внутренний вызов return 0;}
int sign(int x){ if(x > 0){ return 1;} if(x < 0){ return -1;} return 0; // сюда доходим, только если x равно 0}
bool isVowel(char c){ return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';}if(isVowel(s[i])){ vowels++;}
#include <bits/stdc++.h>using namespace std;void printRow(int count, char c){ // void: функция печатает и ничего не возвращает for(int i = 0; i < count; i++){ cout << c;}}int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); int n; cin >> n; for(int i = 1; i <= n; i++){ printRow(n - i, ' '); // пробелы сдвигают ступеньку вправо printRow(i, '#'); cout << "\n";} return 0;}
#include <bits/stdc++.h>using namespace std;int digitCount(long long n){ if(n == 0){ return 1; // 0 записывается одной цифрой} int count = 0; while(n > 0){ count++; n /= 10; // у функции своя копия n} return count;}int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); int q; cin >> q; for(int i = 0; i < q; i++){ long long x; cin >> x; cout << digitCount(x)<< " ";} cout << "\n"; return 0;}
int biggest(int a, int b, int c){ return bigger(bigger(a, b), c);}
int f(int x){ return x * x - 1;}int main(){ cout << f(4) + f(2)<< "\n";}