ПрограммированиеСистемный программист

Раскройте особенности объявления и использования указателей на функции в C. Как правильно хранить массив указателей на разные функции и вызывать их динамически?

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

Ответ

Указатель на функцию в C — переменная, в которой хранится адрес функции, позволяющий динамически выбирать, какую функцию вызвать. Обычное объявление указателя на функцию:

// Указатель на функцию, принимающую int и возвращающую int int (*f_ptr)(int);

Для массива указателей:

int func1(int x) { return x + 1; } int func2(int x) { return x * 2; } int (*f_arr[2])(int) = { func1, func2 }; int result = f_arr[1](10); // вернет 20
  • Такой подход позволяет создавать таблицы команд, обработчиков или автоматически связывать действия с номером операции.
  • Все функции должны иметь одинаковую сигнатуру.
  • При работе с указателями на функции типы должны точно совпадать.

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

Можно ли с помощью указателя на функцию вызвать функцию с другой сигнатурой?

Частый неверный ответ: «Да, если использовать приведение типа».

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

Пример:

void funcA(int x) { printf("A: %d ", x); } void funcB(float y) { printf("B: %f ", y); } void (*fptr)(int) = (void (*)(int)) funcB; fptr(5); // ОШИБКА: будут переданы неверные данные

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


История

В проекте плагин-системы поместили в таблицу указателей функции с разными сигнатурами (одна возвращала int, другая void). При запуске на некоторых архитектурах происходило повреждение стека.

История

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

История

В firmware не проверяли результат смены указателей на функции. Недостаточная инициализация массива обработчиков приводила к вызову нулевого указателя (NULL), после чего устройство зависало без уведомления пользователя.