Функции

Назначение функций

Функция (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 возвращает результат и завершает функцию.

Подтверждение

—