- Функции
- Работа
Функции
Как было сказано, язык Си предоставляет в распоряжение программиста небольшой, но достаточно мощный инструментарий в виде набора операторов, которые позволяют достаточно просто решать определенные наиболее часто требуемые задачи. Однако программист наверняка будет нуждаться и в других решениях «типовых» для его алгоритма задач. Т.е. наверняка в программе найдутся определенные участки, выполняющие конкретные этапы алгоритма и при этом повторяющиеся с минимальными изменениями во многих местах. Для уменьшения таких повторов, а заодно для качественного улучшения реализации алгоритма введено понятие функции.
Функция - это особым образом оформленная последовательность операторов, заменяемая в тексте программы своим коротким эквивалентом, т.е. обращением к функции.
Функция представляет собой как бы мини-программу по обработке небольшого количества данных. Данные, передаваемые ей на обработку, получили название параметров функции, а результат их обработки назван значением функции. Для возврата результата функции служит особый оператор return.
Различают описание (определение) функции, реализацию и ее использование (иначе говоря - вызов, т.е. обращение к функции). Как и переменная, функция должна быть определена до первого обращения к ней.
Шаблон определения функции следующий:
<тип> <идентификатор>([список параметров]);
Здесь тип - любой ранее определенный тип данных, идентификатор - это собственно «имя» описываемой функции, а список параметров - это разделенный запятыми список описаний «входных» переменных.
Описание функции напоминает описание переменной, за исключением обязательно присутствующих круглых скобок (список параметров может отсутствовать). Под типом функции подразумевается тип возвращаемого ею значения.
Реализация функции - это уже раскрытие в виде операторов алгоритма ее работы. Шаблон реализации функции следующий:
<тип> <идентификатор>([список параметров]) {
[список описаний переменных]
[последовательность операторов]
}
Начало реализации аналогично определению, однако завершается не точкой с запятой, а ограниченным фигурными скобками телом функции. Тело функции состоит из необязательного объявления переменных, принадлежащих только этой функции, и необязательной последовательности операторов, реализующих алгоритм работы функции. Необязательность всех элементов лишь подчеркивает, что такое допустимо синтаксисом языка, но вовсе не здравым смыслом.
Наконец, использование функции осуществляется по следующему шаблону:
<идентификатор>([список значений]);
Здесь идентификатор - тот же, что был задан при описании и реализации функции, а список значений - это список выражений, используемых для передачи в функцию параметров. Число выражений в этом списке обязательно должно совпадать с числом параметров в определении функции. Рассмотрим все вышесказанное более подробно на примере.
Предположим, необходимо иметь функцию вычисления квадрата гипотенузы по значению катетов прямоугольного треугольника. Определимся с входными и выходными данными. Очевидно, что значения длин катетов - это входные, а квадрат гипотенузы -выходные данные. Ограничим диапазон значений входных данных типом int, тогда очевидно для выходного значения потребуется тип long. Опишем функцию q_hyp, которая получает на входе два значения типа int, и возвращает результат типа long (воспользуемся шаблоном описания функции):
long q_hyp (int katet1, int katet2);
Далее необходимо выполнить реализацию функции:
long q_hyp(int katet1, int katet2){
long result;
result = katet1*katet1 + katet2*katet2;
return result;
}
Следует обратить внимание на новый оператор return, имеющий очень простой шаблон:
return [возвращаемое значение];
Этот оператор вызывает немедленное завершение работы функции с заданным возвращаемым значением, которым может быть любое выражение.
В реализации функции использована дополнительная переменная result типа long. Эта переменная описана внутри тела функции и называется локальной переменной, т.е. доступной только внутри тела этой функции. Это означает, что вне тела функции переменная result не существует, в противовес глобальным переменным, описанным вне тела функции (в тексте программы), которые существуют и доступны как внутри функции, так и в остальной части программы. Функция может использовать любые глобальные переменные, но ее локальные переменные нигде, кроме ее тела, использовать нельзя.
Как же можно использовать только что определенную функцию? Любым из следующих способов:
int a = 2, b = 5;
long r;
r = q_hyp(2,5); // r будет равно 29
r = q_hyp(a, 4); // r будет равно 20
r = q_hyp(a, b); // r будет равно 29
r = q_hyp(5 + a, b *2); // r будет равно 149
q_hyp(a,b); // результат функции будет проигнорирован
r = 10 * q_hyp(a, b); // г будет равно 290
То есть обращение к функции эквивалентно использованию переменной соответствующего значения. Кстати, если результат функции не используется - это не является ошибкой. Самое важное: переменным, определенным в качестве входных, присваиваются значения, фактически указанные при обращении функции. Говорят, что формальным параметрам присваиваются фактические значения.
Переменные-формальные параметры функции доступны в ее теле наравне с локальными, причем любые их изменения никак не отражаются на «внешней» программе. В только что рассмотренном примере г = q_hyp(a, b) никаким способом функция не может изменить значения переменных а и Ь, даже если бы в ее теле было написано katetl = 100 - действует правило локальности переменной katet1.
Теперь рассмотрим различные допустимые варианты реализации функций.
Функция, которая не возвращает значения.
Для такого случая существует особый вид типа - void (пусто). Этот тип означает буквально «нет значения». Для описанных как void функций недопустимо использование оператора return с указанием возвращаемого значения. Обычно функции без возвращаемого значения изменяют глобальные переменные. Пример:
void func(int x);
Функция без параметров.
Такая функция может как возвращать значение, так и обойтись без этого. При ее описании, реализации и использовании просто указываются круглые скобки с ключевым словом void внутри. Пример:
int func(void);
Следует так же оговорить особенность оператора return. Как было сказано, он вызывает немедленное завершение функции, т.е. в следующем примере функция всегда будет возвращать значение 100 вне зависимости от значения переданных ей параметров:
long q_hyp (int katet1, int katet2);
Далее необходимо выполнить реализацию функции:
long q_hyp(int katet1, int katet2){
long result;
return 100;
result = katet1*katet1 + katet2*katet2;
return result;
}
Если функция не возвращает значения, использование оператора return необязательно - в этом случае функция завершится после исполнения последнего оператора ее тела.
Внутри функции могут быть определены метки для использования совместно с оператором goto. Это локальные метки, т.е. оператор goto может осуществить переход лишь на метку в пределах тела функции, и никуда более. Глобальных меток в Си не существует (см. раздел «setjmp.h - Нелокальные переходы goto» для знакомства со способом обойти это ограничение).