Указатели и память

Указатели на функции и обратные вызовы в C и C++

В этом уроке вы передадите функцию как параметр в другую функцию: через указатели на функции в C и лямбды в C++.

Введение

Обратный вызов (callback) - это передача функции в другую функцию, которая вызовет её, когда понадобится. Так универсальный алгоритм может выполнять поведение, выбранное вызывающим кодом, не зная деталей его реализации. Например, одна и та же функция apply может возводить в квадрат, удваивать или выполнять любое другое преобразование.

Главное

  • Указатель должен указывать только на существующий объект нужного типа или быть равен null.
  • Каждую выделенную область памяти нужно освободить ровно один раз.
  • Явно обозначайте, какая функция владеет памятью (ownership), а какие данные только читаются (const).

Примеры кода на C и C++

C
Запустить →
main.c
#include <stdio.h>

int square(int value) { return value * value; }
int apply(int value, int (*operation)(int)) { return operation(value); }

int main(void) {
    printf("%d\n", apply(6, square));
    return 0;
}
Результат выполнения
36
C++
Запустить →
main.cpp
#include <functional>
#include <iostream>

int apply(int value, const std::function<int(int)>& operation) {
    return operation(value);
}

int main() {
    int factor = 6;
    std::cout << apply(factor, [](int value) { return value * value; }) << '\n';
}
Результат выполнения
36

Сравнение C и C++

В C параметр int (*operation)(int) - это указатель на функцию, которая принимает int и возвращает int, поэтому apply(6, square) возвращает 36. В C++ лямбды и std::function могут хранить более широкий круг вызываемых объектов (callables). В коде, где важна производительность, шаблон часто оказывается лучше std::function.

C

Владение памятью и её освобождение полностью лежат на программисте.

C++

Контейнеры и RAII автоматически освобождают ресурсы, когда заканчивается время жизни объекта.

Упражнения для практики

Запустите обе версии, а затем измените код, чтобы увидеть разницу в безопасности, которую даёт каждый язык.

  • Проверьте работу с нулевыми указателями и при ошибках выделения памяти.
  • Запустите программу с AddressSanitizer и UndefinedBehaviorSanitizer, чтобы найти ошибки работы с памятью.
  • Явно обозначьте, какой указатель владеет памятью, а какой служит только для чтения.

Попрактикуйтесь на задаче

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

Все задачи