C Academy · Урок

Передача функций

Передавайте обратные вызовы как аргументы

Урок 2 из 413 шагов

«Передача функций» — бесплатный урок C Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C Academy содержит 4 уроков всего.

Функции как аргументы

Поскольку указатель на функцию — это обычное значение, функцию можно передать другой функции. Полученная функция называется функцией обратного вызова.

Это позволяет одной процедуре настраивать часть своего поведения.

#include <stdio.h>

int apply(int x, int (*f)(int)) {
    return f(x);
}

int square(int x) { return x * x; }

int main(void) {
    printf("%d\n", apply(6, square));
    return 0;
}

Параметр функции обратного вызова

Параметр функции обратного вызова объявляется так же, как переменная-указатель на функцию. Внутри функции его можно вызывать как любую другую функцию.

#include <stdio.h>

void run_twice(void (*action)(void)) {
    action();
    action();
}

void beep(void) { printf("beep\n"); }

int main(void) {
    run_twice(beep);
    return 0;
}

Замена поведения

Передавайте разные функции обратного вызова, чтобы получать разные результаты от одной и той же функции высшего порядка.

#include <stdio.h>

int combine(int a, int b, int (*op)(int, int)) {
    return op(a, b);
}

int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }

int main(void) {
    printf("%d\n", combine(3, 4, add));
    printf("%d\n", combine(3, 4, mul));
    return 0;
}

Применение к массиву

Функция map применяет функцию обратного вызова к каждому элементу массива непосредственно в нём.

#include <stdio.h>

void map(int *a, int n, int (*f)(int)) {
    for (int i = 0; i < n; i++) a[i] = f(a[i]);
}

int inc(int x) { return x + 1; }

int main(void) {
    int a[] = {1, 2, 3};
    map(a, 3, inc);
    for (int i = 0; i < 3; i++) printf("%d ", a[i]);
    printf("\n");
    return 0;
}

Фильтрация с помощью предиката

Предикат — это функция обратного вызова, возвращающая значение true или false. Используйте его, чтобы подсчитывать или выбирать подходящие элементы.

#include <stdio.h>

int count_if(int *a, int n, int (*pred)(int)) {
    int c = 0;
    for (int i = 0; i < n; i++) if (pred(a[i])) c++;
    return c;
}

int is_even(int x) { return x % 2 == 0; }

int main(void) {
    int a[] = {1,2,3,4,5,6};
    printf("%d\n", count_if(a, 6, is_even));
    return 0;
}

Сведение к одному значению

Функция reduce сворачивает массив в одно значение с помощью бинарной функции обратного вызова и начального аккумулятора.

#include <stdio.h>

int reduce(int *a, int n, int init, int (*op)(int,int)) {
    int acc = init;
    for (int i = 0; i < n; i++) acc = op(acc, a[i]);
    return acc;
}

int add(int a, int b) { return a + b; }

int main(void) {
    int a[] = {1,2,3,4};
    printf("%d\n", reduce(a, 4, 0, add));
    return 0;
}

Передача функции сравнения

Для сортировки и поиска часто передают функцию обратного вызова для сравнения, чтобы один и тот же алгоритм работал с любым порядком элементов.

#include <stdio.h>

int max_by(int a, int b, int (*greater)(int,int)) {
    return greater(a, b) ? a : b;
}

int gt(int a, int b) { return a > b; }

int main(void) {
    printf("%d\n", max_by(3, 8, gt));
    return 0;
}

typedef для типов функций обратного вызова

Имя типа функции обратного вызова, заданное с помощью typedef, делает сигнатуры функций более понятными.

#include <stdio.h>

typedef int (*IntFn)(int);

int apply(int x, IntFn f) { return f(x); }

int neg(int x) { return -x; }

int main(void) {
    printf("%d\n", apply(5, neg));
    return 0;
}

Запасной вариант для функции обратного вызова

Если функция обратного вызова может иметь значение NULL, предусмотрите поведение по умолчанию, чтобы вызывающая сторона могла отказаться от неё.

#include <stdio.h>

int apply(int x, int (*f)(int)) {
    if (f == NULL) return x;
    return f(x);
}

int dbl(int x) { return x * 2; }

int main(void) {
    printf("%d\n", apply(9, NULL));
    printf("%d\n", apply(9, dbl));
    return 0;
}

Функции обратного вызова разделяют код

Функции обратного вызова позволяют общему алгоритму не зависеть от конкретной операции. Библиотека предоставляет цикл, а Вы — поведение.

#include <stdio.h>

void for_each(int *a, int n, void (*visit)(int)) {
    for (int i = 0; i < n; i++) visit(a[i]);
}

void show(int x) { printf("[%d]", x); }

int main(void) {
    int a[] = {7, 8, 9};
    for_each(a, 3, show);
    printf("\n");
    return 0;
}

Выбор функции обратного вызова во время выполнения

Объедините выбор по условию с передачей функции: выберите функцию обратного вызова, а затем передайте её функции высшего порядка.

#include <stdio.h>

int apply(int x, int (*f)(int)) { return f(x); }
int sq(int x) { return x * x; }
int cube(int x) { return x * x * x; }

int main(void) {
    int power = 3;
    int (*f)(int) = (power == 2) ? sq : cube;
    printf("%d\n", apply(2, f));
    return 0;
}

Быстрая проверка

Проверьте, насколько хорошо Вы понимаете передачу функций.

Итоги

Вы научились передавать функции как функции обратного вызова:

  • Объявляйте параметр функции обратного вызова так же, как указатель на функцию.
  • Создавайте функции высшего порядка, такие как map, filter, reduce и for_each.
  • Используйте typedef, чтобы давать имена типам функций обратного вызова.
  • Защищайтесь от функций обратного вызова со значением NULL с помощью поведения по умолчанию.
Можно начать бесплатно

Изучай C с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
39
Уроки
144

Часто задаваемые вопросы

Урок «Передача функций» бесплатный?

Да — полный текст урока «Передача функций» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C Academy, подпишись на CoddyKit PRO. Курс C Academy содержит 4 уроков всего.

Чему я научусь в уроке «Передача функций»?

Передавайте обратные вызовы как аргументы Ты практикуешь C Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C Academy?

Предыдущий опыт не требуется. C Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Передача функций»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C Academy?

Да. Каждый урок C Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Объявление указателей на функции
  2. Передача функций
  3. qsort с компараторами
  4. Таблицы указателей на функции
← Назад к C Academy