ПрограммированиеEmbedded C разработчик

Опишите, как реализуются и работают функции обратного вызова (callback functions) в языке C. Как правильно объявлять и использовать такие функции при разработке библиотек или взаимодействии с API?

Проходите собеседования с ИИ помощником Hintsage

Ответ

Функции обратного вызова (callbacks) — это функции, адрес которых передается как аргумент другой функции. Это позволяет реализовать обработчики событий, пользовательские алгоритмы и плагины.

Объявление callback-функции:

  1. Описывается соответствующий тип указателя на функцию:
typedef void (*callback_func_t)(int);
  1. Передача функции-обработчика:
void process(callback_func_t cb) { // ... cb(42); // вызов callback } void handler(int n) { printf("Обработано число: %d ", n); } int main() { process(handler); return 0; }

Советы:

  • Придерживайтесь явного typedef для типов указателей, код станет читабельнее.
  • Убедитесь, что сигнатура callback-функции совпадает с ожидаемой.
  • Избегайте передачи локальных неинициализированных или освобождённых функций.

Вопрос с подвохом

Можно ли передавать функцию с несовпадающей сигнатурой в качестве callback-а?

Частый ошибочный ответ: «Да, C позволит, если объявить явное приведение типа».

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

Пример опасности:

typedef void (*cb_t)(int); void wrong_cb(double d) { printf("%f ", d); } void call(cb_t f) { f(123); } int main() { call((cb_t)wrong_cb); } // ОПАСНО: сигнатуры различаются

Примеры реальных ошибок из-за незнания тонкостей темы


История

В одном встраиваемом проекте функция сортировки получала в качестве compare-функции указатель на функцию со слишком узкой сигнатурой. Это привело к ошибкам сортировки и повреждению памяти из-за некорректной передачи параметров.

История

В библиотеке графического движка был реализован механизм событий на основе callback-ов, но часть callback-ов случайно ссылалась на локальные функции из уже освобождённых динамических библиотек, что вызывало вылеты при встрече с невалидными адресами.

История

При разработке кросс-платформенной системы автор неверно определил calling convention для callback-функций. Это не проявлялось на одной ОС, но приводило к краху программы на другой при вызове callback-ов из Си-библиотеки.