В задачe вычисления числа сочетаний из n элементов по k, необходимо вычисление факториалов трех величин: n, k и n - k. Для этого можно сделать три цикла, что приводит к увеличению размера программы за счет трехкратного повторения похожего кода. Вместо этого лучше сделать одну функцию, вычисляющую факториал любого данного числа n и трижды использовать эту функцию в своей программе. Соответствующая функция может выглядеть так:
int factorial(int n) { int f = 1, i; for (i = 2; i <= n; ++i) { f = f * i; } return f; }
Этот текст должен идти до основной программы, то есть до функции main()
.
Первая строчка этого примера является описанием нашей функции. factorial
– идентификатор,
то есть имя нашей функции. После идентификатора в круглых скобках идет список параметров,
которые получает наша функция. Список состоит из перечисленных через запятую типов параметров и их
идентификаторов. В нашем случае список состоит из одной величины n
, имеющей тип int
:
наша функция вычисляет факториал целого числа. Функция вычисляет целочисленную величину,
поэтому функция будет возвращать значение типа int
, что указывается до идентификатора функции.
Функция может не возвращать никакого значения, тогда в качестве типа возвращаемого значения должно быть
указано слово void
.
Далее идет тело функции в фигурных скобках. Внутри функции вычисляется значение факториала числа n
и оно сохраняется в переменной f
. Функция завершается инструкцией return f
, которая
завершает работу функции и возвращает значение переменной f
. Инструкция return
может встречаться
в произвольном месте функции, ее исполнение завершает работу функции и возвращает указанное значение в место вызова.
Если функция не возвращает значения, то инструкция return
используется без возвращаемого значения, также
в функциях, не возвращающих значения, инструкция return
может отсутствовать.
Функция должна быть описана до начала основной программы. Сама же основная программа, как можно догадаться,
также является функцией с именем main
, не получающая никаких параметров и возвращающее значение типа int
.
Теперь мы можем использовать нашу функцию factorial
в основной программе нахождения числа сочетаний:
int main() { int n, k; cin >> n >> k; cout << factorial(n) / (factorial(k) * factorial(n - k)) << endl; return 0; }
В этом примере мы трижды вызываем функцию factorial
для вычисления трех факториалов:
factorial(n)
, factorial(k)
, factorial(n - k)
.
Мы также можем объявить функцию binomial
, которая принимает два целочисленных параметра
n
и k
и вычисляет число сочетаний из n
по k
:
int binomial(int n, int k) { return factorial(n) / (factorial(k) * factorial(n - k)); }
Тогда в нашей основной программе мы можем вызвать функцию binomial
для нахождения числа сочетаний:
cout << binomial(n,k) << endl;
Поскольку в этом случае функция main
вызывает функцию binomial
, а функция binomial
вызывает функцию factorial
, а каждая функция должна быть описана до ее вызова из другой функции,
то порядок описания функций в программе должен быть такой:
int factorial(int n)
int binomial(int n, int k)
int main()
Вернемся к задаче нахождения наибольшего из двух или трех чисел. Напишем функцию, находящую максимум из двух данных чисел:
int max(int a, int b) { if (a > b) { return a; } else { return b; } }
Теперь мы можем реализовать функцию max
, находящую максимум трех чисел:
int max(int a, int b, int c) { return max(max(a, b), c); }
В данном примере написаны две различные функции max
: первая с двумя параметрами,
вторая с тремя параметрами. Несмотря на то, что функции имеют одинаковые имена, по количеству
передаваемых параметров ясно, какая из них имеется в виду. В нашем случае функция
max(int a, int b, int c)
дважды вызывает функцию max
для двух чисел:
сначала, чтобы найти максимум из a
и b
, потом чтобы найти максимум из этой
величины и c
.
В каждой функции объявляется свой набор переменных. Переменные, определенные внутри каждой из функций, называются локальными переменными данной функции. Если в двух разных функциях есть локальные переменные с одинаковыми именами, то это — различные переменные. Если в одной из функций поменять значение этой переменной, то в другой функции оно останется неизменным. Пример:
void f1() { int a; a = 1; return; } int main() { int a; a = 2; f1(); cout << a << endl; return 0; }
На экран будет выведено значение 2, т.к. изменение локальной переменной a
внутри функции f1
не приводит
к изменению значения локальной переменной a
для функции main
.
Переменная может быть объявлена и вне всякой функции, тогда она будет доступной из всех функций и в каждой функции это будет одна и та же переменная. Такие переменные называются глобальными.
int a; void f1() { a = 1; return; } int main() { a = 2; f1(); cout << a << endl return 0; }
В этом примере на экран будет выведено значение 1, поскольку вызов функции f1
изменил значение глобальной переменной a
.
Одно из свойств глобальных переменных — их значения по умолчанию проинициализированы нулем, в отличии от локальных переменных, чьи значения по умолчанию инициализируются мусором.
Широкое использование глобальных переменных не принято в современном программировании, вместо глобальных переменных лучше использовать локальные переменные, а для обмена информацией между функциями нужно использовать передаваемые по ссылке параметры.
Раньше у нас была задача, в которой требовалось поменять значения двух переменных. Поскольку такое нужно будет довольно часто, попробуем написать для этого функцию.
void Swap(int a, int b) { int t = a; a = b; b = t; return; } int main() { int a = 1, b = 2; Swap(a, b); cout << a << " " << b << endl; return 0; }
Эта программа выведет 1 2
, то есть значения переменных a
и b
не поменяются. Причина в том, что передаваемые
в функцию параметры являются локальными переменными, когда вызывается функция, то создаются две локальные переменные
a
и b
для этой функции, в них записываются значения локальных переменных
a
и b
функции main
, затем запускается функция Swap
,
которая меняет значения своих локальных переменных, при этом значения локальных переменных функции main
не меняются. Такая передача параметров функции называется передачей параметров по значению.
Можно было бы объявить a
и b
глобальными переменными, но тогда мы не смогли бы использовать
функцию для обмена значений двух переменных, отличных от a
и b
.
Для того, чтобы функция могла изменять значение переменной, переданной в качестве параметра при вызове, используется механизм передачи параметров по ссылке. Если переменная передается в функцию по ссылке, то функция работает с самой переменной, а не с ее локальной копией и может изменять ее значения. Для обозначения того, что параметр передается по ссылке, перед идентификатором переменной-параметра, в описании функции, необходимо поставить знак амперсанда &:
void Swap(int & a, int & b) { int t = a; a = b; b = t; return; }Теперь функцию
Swap
можно использовать для обмена значений любых двух переменных типа int
,
при этом называться они могут как угодно, например, можно вызывать функцию так: Swap(x, y)
.
Но нельзя вызывать функцию так: Swap(1, 2)
—передаваемые по ссылке параметры должны
быть переменными, а не числами или какими-то более сложными выражениями.
Механизм передачи параметров по ссылке используется также в случае, когда функция должна вернуть не одно значение, а несколько. Тогда в функцию необходимо по ссылке передать несколько переменных, в которые функция запишет результат своей работы.