Урок 7. Функции и ссылки
Когда одно и то же действие нужно в нескольких местах программы, его выносят в функцию — именованный кусок кода, который можно вызвать по имени. main — тоже функция, а sqrt или max — функции из стандартной библиотеки. Теперь пора писать свои.
Как устроена функция
#include <iostream>
using namespace std;
int square(int x) {
return x * x;
}
void printLine(int length) {
for (int i = 0; i < length; i++) {
cout << "-";
}
cout << "\n";
}
int main() {
cout << square(7) << "\n";
int a = 3;
cout << square(a + 1) << "\n";
printLine(10);
return 0;
}49
16
----------Из чего состоит функция:
- тип результата —
intуsquare. Если функция ничего не возвращает, пишутvoid; - имя —
square, по тем же правилам, что имена переменных; - параметры в скобках —
int x. Их может быть несколько через запятую или ни одного:int f(); - тело в фигурных скобках.
returnзаканчивает функцию и отдаёт значение тому, кто её вызвал.
Значения, с которыми функцию вызывают (7, a + 1), называются аргументами. При вызове square(a + 1) параметр x получает значение 4.
В функции, которая что-то возвращает, return должен сработать на любом пути выполнения. Если для какого-то случая его нет, результат непредсказуем; -Wall предупредит: control reaches end of non-void function.
Функция, которая отвечает «да» или «нет»
Функция может возвращать bool. Проверка числа на простоту — классика:
#include <iostream>
using namespace std;
bool isPrime(int n) {
if (n < 2) {
return false;
}
for (int d = 2; d * d <= n; d++) {
if (n % d == 0) {
return false;
}
}
return true;
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
if (isPrime(i)) {
cout << i << " ";
}
}
cout << "\n";
return 0;
}302 3 5 7 11 13 17 19 23 29return false внутри цикла сразу выходит из функции — дальше проверять делители не нужно. Делители ищутся только до корня из n (d * d <= n): если у числа есть делитель больше корня, то есть и парный меньше корня.
Сначала объявить, потом вызвать
Компилятор читает файл сверху вниз. Функцию, которую вызывают в main, надо написать выше main — иначе ошибка was not declared in this scope. Если хочется держать main первой, выше ставят только объявление (прототип) — первую строку функции с точкой с запятой:
#include <iostream>
using namespace std;
int cube(int x); // объявление
int main() {
cout << cube(3) << "\n";
return 0;
}
int cube(int x) { // определение
return x * x * x;
}27Объявление без определения соберётся, но на последнем шаге сборки даст ошибку undefined reference — об этом справка Ошибка undefined reference.
Передача по значению
Параметр — копия аргумента. Что бы функция ни делала с параметром, переменная снаружи не меняется:
#include <iostream>
using namespace std;
void tryChange(int x) {
x = 100;
}
int main() {
int a = 5;
tryChange(a);
cout << a << "\n";
return 0;
}5Ссылки
Ссылка — второе имя для уже существующей переменной. Объявляется со знаком & после типа:
#include <iostream>
using namespace std;
void swapValues(int& a, int& b) {
int t = a;
a = b;
b = t;
}
int main() {
int x = 1, y = 2;
int& r = x; // r — другое имя для x
r = 10;
cout << x << "\n";
swapValues(x, y);
cout << x << " " << y << "\n";
return 0;
}10
2 10Параметр int& a — не копия, а сама переменная вызывающего. Поэтому swapValues меняет x и y местами. Так функция может вернуть несколько результатов: через параметры-ссылки. В стандартной библиотеке такая функция уже есть — swap(x, y).
Аргументом параметра-ссылки может быть только переменная: swapValues(1, 2) не соберётся — числу 1 нельзя присвоить новое значение.
Ссылка на const — для больших данных
Передача вектора или строки по значению копирует их целиком. Для вектора из миллиона чисел это долго, а если функция вызывается много раз — очень долго. Решение — const и &:
#include <iostream>
#include <vector>
using namespace std;
long long total(const vector<int>& v) {
long long s = 0;
for (int x : v) {
s += x;
}
return s;
}
int main() {
vector<int> data = {4, 8, 15, 16, 23, 42};
cout << total(data) << "\n";
return 0;
}108const vector<int>& v — функция работает с самим вектором без копирования, а const запрещает его менять. Правило: простые типы (int, double, char) передавайте по значению, а vector, string и другие большие объекты — по const-ссылке, если функция их не меняет, и по обычной ссылке, если меняет.
Перегрузка
Несколько функций могут называться одинаково, если у них разные параметры. Компилятор выберет нужную по аргументам:
#include <iostream>
#include <string>
using namespace std;
void show(int x) {
cout << "int " << x << "\n";
}
void show(double x) {
cout << "double " << x << "\n";
}
void show(const string& s) {
cout << "string " << s << "\n";
}
int main() {
show(3);
show(2.5);
show(string("hi"));
return 0;
}int 3
double 2.5
string hiРекурсия
Функция может вызывать сама себя — это рекурсия. Факториал: n! = n · (n − 1)!, а 0! = 1.
#include <iostream>
using namespace std;
long long factorial(int n) {
if (n == 0) {
return 1; // база рекурсии
}
return n * factorial(n - 1);
}
int main() {
cout << factorial(5) << "\n";
cout << factorial(20) << "\n";
return 0;
}120
2432902008176640000У рекурсии обязательно должна быть база — случай, когда функция больше не вызывает себя. Без неё вызовы идут, пока не кончится память для них (стек), и программа падает. То же бывает и при правильной рекурсии, но очень глубокой — в сотни тысяч вызовов и больше.
Локальные и глобальные переменные
Переменные, объявленные внутри функции или блока { }, — локальные: они существуют только там. У каждой функции свои локальные переменные, и одинаковые имена в разных функциях не мешают друг другу. Переменные вне всех функций — глобальные, они видны везде. На олимпиадах глобальными часто делают большие массивы, но в остальном лучше передавать данные через параметры: так понятнее, кто что меняет.
Коротко
- Функция:
тип имя(параметры) { ... return значение; }; без результата —void. - Функцию пишут выше места вызова или ставят выше её объявление.
- По значению — копия; по ссылке
int&— сама переменная, её можно менять. - Большие объекты передавайте по
const-ссылке: без копирования и без изменений. - Перегрузка — одинаковые имена с разными параметрами.
- Рекурсии нужна база, иначе программа упадёт.