Функции
Назначение функций
Функция (function) — это именованная часть программы, которая решает отдельную задачу: её вызывают по имени, передают данные и получают результат.
Когда вся программа находится в main, её трудно читать, проверять и править. Функции решают
эту проблему:
- читаемость —
mainпревращается в короткий список шагов; - повторное использование — одну функцию вызывают много раз, не копируя код;
- отладка — ошибку ищут в одной небольшой функции, а не во всей программе.
Аналогия. Функция — как отдельный инструмент в ящике: у него есть имя, им можно пользоваться много раз, и он не мешает другим инструментам.
main— рабочее место, где инструменты применяют по очереди.
Объявление и определение функции
Определение (definition) функции — это её полное описание: заголовок и тело.
тип_возврата имя_функции(параметры)
{
/* тело функции */
}
Например, функция, возвращающая большее из двух чисел:
int max_of_two(int x, int y)
{
if (x > y)
{
return x;
}
return y;
}
| Часть | Что это | Пример |
|---|---|---|
| тип возврата | что функция возвращает (тип результата) | int, double; если ничего — void |
| имя | по смыслу задачи | max_of_two, is_even, sum_to |
| параметры | данные «на вход» | (int x, int y); если параметров нет — (void) |
return | вернуть результат и завершить функцию | return sum; |
Если функция ничего не возвращает, указывают тип void:
void print_line(int n, char ch)
{
for (int i = 0; i < n; i++)
{
printf("%c", ch);
}
printf("\n");
}
У функции с типом void return может быть без значения (return;) или отсутствовать.
Объявление (declaration) — это та же функция без тела. Объявление, размещённое до первого вызова, называют прототипом (prototype).
Прототип: сообщаем компилятору заранее
Компилятор обрабатывает файл сверху вниз, поэтому к моменту вызова он уже должен знать
имя функции, тип результата и типы параметров. Для этого до main пишут прототипы:
#include <stdio.h>
int max_of_two(int x, int y);
int is_even(int n);
void print_line(int n, char ch);
int main(void)
{
/* ... */
}
После прототипов определения функций можно располагать в любом порядке — в том числе после
main.
Важно. Прототип и определение должны совпадать по имени, типу результата и типам параметров. Иначе компилятор будет считать их разными функциями.
Аргументы и параметры
В объявлении функции перечисляются параметры (parameters) — это «входные» переменные функции. При вызове на их место подставляются аргументы (arguments) — конкретные значения или переменные:
int max_of_two(int x, int y); /* x, y — параметры */
int m = max_of_two(a, b); /* a, b — аргументы */
Параметры ведут себя как локальные переменные: они видны только внутри своей функции.
Передача по значению
Обычные переменные (int, double, char) передаются в функцию по значению (pass by
value): функция получает копию значения. Изменение копии не влияет на переменную
в вызывающем коде:
void inc(int x)
{
x = x + 1; /* изменяется только копия */
}
int main(void)
{
int a = 5;
inc(a);
printf("%d\n", a); /* будет напечатано 5, а не 6 */
return 0;
}
Чтобы функция могла изменить переменную вызывающего кода, нужен другой механизм. Для массивов он работает иначе — об этом в статье «Передача массивов в функции».
Область видимости переменных
Переменные, определённые внутри функции, видны только в ней:
int sum_to(int n)
{
int sum = 0; /* своя sum, видна только здесь */
for (int i = 1; i <= n; i++)
{
sum += i;
}
return sum;
}
В main может быть своя переменная sum — это разные переменные, они не конфликтуют.
Поэтому одну и ту же функцию можно вызывать сколько угодно раз.
Глобальные переменные в этой работе использовать не нужно: всё, что требуется функции,
передаётся через параметры, а результат возвращается через return.
Программа из функций: пример
#include <stdio.h>
int max_of_two(int x, int y);
int is_even(int n);
int main(void)
{
int a, b;
printf("Введите два числа: ");
if (scanf("%d %d", &a, &b) != 2)
{
printf("Ошибка ввода\n");
return 1;
}
printf("Большее: %d\n", max_of_two(a, b));
if (is_even(a))
{
printf("%d чётное\n", a);
}
return 0;
}
int max_of_two(int x, int y)
{
if (x > y)
{
return x;
}
return y;
}
int is_even(int n)
{
return n % 2 == 0;
}
Программа читается как последовательность шагов: main управляет порядком работы, а каждая
функция отвечает за одну задачу.
Частые ошибки
- забыли прототип — компилятор предупреждает об «implicit declaration» и может собрать программу неправильно;
- прототип и определение не совпадают по параметрам — при компоновке получится ошибка;
- указали тип
void, но пытаетесь использовать результат в выражении — результата нет; - забыли
returnв функции, которая должна возвращать значение; - изменили обычный параметр и ожидаете изменений в
main— так нельзя (для этого нужен указатель или другой механизм); - продублировали одну и ту же обработку вместо вызова общей функции.
Что запомнить
- функция — именованная часть программы для одной задачи;
- объявление — без тела, определение — с телом; прототипы размещают до
main; - аргументы — при вызове, параметры — в заголовке функции;
- обычные параметры передаются по значению (копией);
- локальные переменные видны только внутри своей функции;
void— функция ничего не возвращает;returnвозвращает результат и завершает функцию.