Все о функциях в языке Си
Функции — это важный инструмент в программировании, который избавляет человека от необходимости повторять одни и те же действия при написании кода. Функция представляет собой блок кода с именем. Имя является ссылкой на участок памяти, где были объединены группа операторов в блок.
Функция вызывается по ссылке и происходит поочередное выполнение операторов в блоке. Суть функции в том, что, написав ее один раз, воспользоваться можно бесконечное количество раз без необходимости писать операторы блока заново.
Объявление функции
Прежде, чем воспользоваться функцией, ее нужно объявить и инициализировать. Стоит сразу отметить, что функция может возвращать результат, состоящий из одного литерала или коллекции, а также может только выполнять определенные в ней операторы, ничего не возвращая.
Пример объявления функции с возвратом и без:
int Func_1(){ // функция которая возвращает литерал типа int
// операторы
return int; // оператор возврата некого значения int
}
void Func_2(){ // функция, которая ничего не возвращает, но выполняет определенные действия
// операторы
}
Пояснение:
- int — может быть любой тип данных, который ставится перед именем функции и говорит о том, какое значение функция возвращает.
- void — ставится перед именем функции и говорит о том, что функция ничего не возвращает.
- Func_1, Func_2 — произвольное имя функции, по которому она вызывается. Также это ссылка на область памяти, где находится блок операторов функции.
- () — скобки служат для параметров (аргументов), функции бывают с параметрами и без. Также скобки используют для вызова функции.
- { // операторы } — блок операторов, который выполняется при вызове функции.
- return — оператор, указывающий на возврат определенного результата функции.
Пример использования функции с возвратом:
int func(){
int x = 5;
int y = 5;
return x+y;
}
func(); // так выглядит вызов функции — сложение выполнилось, вернулась сумма
printf(func()); // Выведет 10
int a = func(); // сохраним в переменную результат вызова функции
printf(a); // выведет 10 как значение переменной, в которую был сохранен результат выполнения функции
Пример использования функции без возврата:
void write_message(){
printf(«Hello World! «);
printf(«Hello everyone! «);
printf(«I glad to see you all!»);
}
write_message();
// Вывод: Hello World! Hello everyone! I glad to see you all!
Для чего нужны функции?
Функции экономят время программиста, а также, в зависимости от параметров выдают определенный результат.
Сравните как бы пришлось писать, не используя функцию:
printf(«Hello World! «);
printf(«Hello everyone! «);
printf(«I glad to see you all!»);
printf(«Hello World! «);
printf(«Hello everyone! «);
printf(«I glad to see you all!»);
printf(«Hello World! «);
printf(«Hello everyone! «);
printf(«I glad to see you all!»);
Здесь мы повторили одни и те-же действия несколько раз. Ведь мало кто захочет, постоянно повторять написание этих операторов в нужном участке кода.
Теперь запишем предыдущий пример в виде функции:
void write_message(){
printf(«Hello World! «);
printf(«Hello everyone! «);
printf(«I glad to see you all!»);
}
write_message();
write_message();
write_message();
Здесь у нас получился тот-же результат, что и в примере без функции. При это, сколько строчек кода было сэкономлено. Функцию можно вызывать в любом участке кода, делать ее условной единицей, а также использовать в других функциях.
Здесь при вызове функции, write_message является ссылкой на инициализацию функции, которую мы осуществили выше вызова. Оператор () осуществляет выполнение блока команд инициализированной функции. Каждый раз как мы вызываем функцию, выполняются команды в описанном блоке.
Параметры функций
Параметры функций позволяют разнообразить результат каждого выполнения функции. В предыдущих примерах мы не использовали параметры, поэтому каждый вызов функции возвращал один и тот-же результат, а функции без возврата обрабатывали одни и те-же литералы.
Синтаксис функции с параметрами:
int/void func(int x, int y){ операторы, return}
В скобках определены две переменные int x и int y. В инициализации функции описывается как будут использоваться эти параметры. При вызове функции параметры получают любое значение в соответствии со своим типом.
Пример:
int func(int x, int y){ // Объявили функцию с параметрам — имена параметров могут быть произвольными
int z = x+y; // используем имена параметров в теле функции — в нашем случае будет произведено сложение значений в параметре
return z;
}
func(5, 5); // параметр x = 5, параметр y = 5, функция сложила значения этих параметров и вернула 10
func(34, 112); // в следующем вызове можно установить новые значения параметров. Возвращенный результат равен 146
func(10, 5); // 15
Однако возвращаемое значение не всегда может быть int. Это может быть любой литерал, соответствующий типам данных, которые находятся в распоряжении языка Cи.
Пример со строкой без возврата:
void fun_str(String a, String c){
String h = a + c + » Yes, That is right!»;
printf(h);
}
fun_str(«I am», «smartest man.»); // Вывод: I am smartest man. Yes, That is right!
Параметров при объявлении в функции может быть сколько угодно. Главное, их количество и тип должны соответствовать при вызове функции. Если при объявлении функции предусмотрено 4 параметра, то и при ее вызове их также должно быть 4, при этом, того же значения, что и при объявлении.
Локальные и глобальные переменные
Локальные переменные обладают областью видимости только внутри функции. Глобальные переменные объявляются и инициализируется вне функции, но в пределах главной функции Main. Если глобальные переменные передать в функцию в качестве аргументов, то их изменение будет проведено только в рамках функции. Значения глобальных переменных останутся не измененными.
Пример:
int a = 50; // a и b — глобальные переменные
int b = 100;
int func(int x, int y, int c){
x +=20; // изменяем локальные переменные
y+=20;
z = x + y + c;
return z;
}
func(a, b, 30); // результат будет равен 210 — в качестве параметров вставили глобальные переменные
printf(a); // 50 — глобальная а осталась неизмененная
printf(b); // 100 — глобальная b осталась с прежним значением
Для глобальных и локальных переменных отводятся разные участки памяти. После того, как функция сработала, все локальные переменные уничтожаются, а глобальные остаются с текущим значением, так как они находятся в другом контексте и в отдельном участке памяти. Передавая в качестве аргументов глобальные переменные, функция получает значение, но не ссылку на память хранения этих переменных.
Функции могут быть выполнены и объявлены в теле другой функции
Вложенные функции можно объявить и вызвать в теле другой функции.
Пример:
void fun(int x){
void f(){ // объявление вложенной функции
printf(x);
}
if(x>5) f(); // если условие верно, то вызов внутренней функции
else printf(«Wrong value»); // иначе — вывод сообщения
}
fun(6); // Wrong value
После того, как главная функция отработала, вложенная также, как и локальная переменная уничтожается и нигде не фиксируется в памяти
Рекурсивные функции
Рекурсия — функция, которая вызывает самому себя. Это функциональный вид цикла, только осуществляемый за счет функции.
Пример:
int recursion(int a)
{
if (a == 1)
{
return 1;
}
else
{
return n * recursion(n — 1);
}
}
recursion(6) // вернет 720
Что здесь произошло:
Мы установили параметр а = 6. Если a равно 1, то выход из функции. В ином случае: возврат (значение параметра умножить на вызов функции, где параметр -1).
Ключевое значение здесь играет n * recursion(n-1): 6 * выполнить инструкции еще раз с параметром 6 — 1. Здесь n перемножается по цепочке: 6*5*4*3*2 = 720. Когда n = 1, то последнее умножение прерывается и возвращается результат из предыдущего звена. В нашем случае, функция recursion повторила свое выполнение 4 раза, на 5-м процесс прервался.
Конечно это простой алгоритм для понимания самой механики рекурсии. На деле же рекурсивные функции используются для построения графиков, подсчета факториала числа, создания таблиц и многое другое. Здесь в теле функции можно использовать всевозможные операторы и подвергать код большому количеству условий. Однако большинство программистов отдают предпочтение циклам.
Но, рекурсивная функция экономичнее — она позволяет устанавливать любые параметры, а ее вызов занимает всего одну строку. Это достаточно удобно, нежели писать несколько циклов. Тем не менее, если необходимо выполнить цепочку расчетов лишь один раз, то рекомендуется все-таки использовать циклы, ведь они удобнее для этих целей. Ошибка в рекурсии может заставить программиста искать ее причину и решение долгое время, когда в цикле данный процесс обычно протекает быстрее ввиду более красивого синтаксиса.
Подводя итоги
Функции — один из самых лучших способов экономии времени на написание программы. Объявив и инициализировав блок один раз, его можно вызывать всего в одну строчку. Функции позволяют пользоваться параметрами, а в блоке можно создавать сложные алгоритмы для поиска решений при определенных условиях.
Функцию всегда можно изменить, коснувшись лишь ее блока. Без нее пришлось бы изменять множество инструкций в коде. Так что функция еще представляет собой прототип блока с кодом. На основе прототипа, работают все вызова конкретной функции.
Также функции позволяют разделить программы на объекты и их поведение. Ввиду того, что Си не объектно-ориентированный язык, функциональное программирование упрощает создавать ассоциации с объектами реального мира. Здесь нет методов и нет полей. Однако всегда можно выделить отдельные переменные и функции для какой-либо сущности.
К примеру, мы программируем движение автомобиля. Для показателей скорости и ускорения используем отдельные переменные, для движения функции — они отвечают за изменение скорости с течением времени и создают видимость движения автомобиля. Как же пришлось программировать движение автомобиля без функций? Правильно, пришлось бы писать код высотой с Эльфелеву Башню.