Какая польза от указателей на функции?
Возможный дубликат:
В чем смысл указателей на функции?
Я пытаюсь понять, где в практических сценариях используются указатели на функции.
а также кто-нибудь может дать мне практический пример, когда мы должны передать функцию как аргумент другой функции.
Ответы
Ответ 1
Указатели на функции могут быть полезны, когда вы хотите создать механизм обратного вызова, и вам нужно передать адрес функции другой функции.
Они также могут быть полезны, когда вы хотите сохранить массив функций, например, для динамического вызова.
Ответ 2
Одним из распространенных применений является реализация функции обратного вызова .
Попробуйте отсортировать что-нибудь, используя библиотечную функцию qsort
. Последний параметр - указатель на написанную вами функцию компаратора.
Ответ 3
Процедуры обратного вызова являются наиболее распространенным сценарием, представленным до сих пор. Тем не менее, есть много других...
Конечные государственные машины, где элементы (многомерные) массивы указывают процедуру, которая обрабатывает/обрабатывает следующее состояние. Это сохраняет определение FSM в одном месте (массив).
Включение функций и отключение функций можно выполнить с помощью указателей функций. У вас могут быть функции, которые вы хотите включить или отключить, которые выполняют похожие, но разные вещи. Вместо того, чтобы заполнять и загромождать ваш код конструкциями тестирования if-else, вы можете закодировать его так, чтобы он использовал указатель на функцию, а затем вы можете включать/отключать функции, изменяя/назначая указатель функции. Если вы добавляете новые варианты, вам не нужно отслеживать все ваши случаи if-else или switch (и не хватает риска); вместо этого вы просто обновляете свой указатель на функцию, чтобы включить новую функцию, или отключите старый.
Уменьшение помех кода Я затронул это в предыдущем примере. Примеры, такие как...
switch (a) {
case 0:
func0();
break;
case 1:
func1();
break;
case 2:
func2();
break;
case 3:
func3();
break;
default:
funcX();
break;
}
Может быть упрощено...
/* This declaration may be off a little, but I am after the essence of the idea */
void (*funcArray)(void)[] = {func0, func1, func2, func3, funcX};
... appropriate bounds checking on 'a' ...
funcArray[a]();
Есть еще много. Надеюсь, это поможет.
Ответ 4
Первое, что приходит мне на ум как очень полезное приложение, - это кнопка. Возьмите следующий код:
int buttonID = CreateButton ("Click Me!", 100, 100, 200, 100, onClick);
Это создаст кнопку в (100,100) с шириной 200 и высотой 100. Каждый раз, когда вы нажимаете на нее, вызывается onClick.
Я использую что-то подобное в личной оболочке Windows API. Это упрощает создание кнопок и т.д.
Ответ 5
Существует два основных применения указателей функций:
- обратные вызовы - используются для обработчиков событий, специализации парсера, передачи функции компаратора...
- плагины и расширения - указатели на функции, предоставляемые плагинами или расширениями библиотек, собираются стандартным functio
GetProcAddress
, dlsym
или похожим, которые принимают идентификатор функции как имя и возвращают указатель функции. Абсолютно важно для API, таких как OpenGL.
Ответ 6
Хорошо, ответ №1: qsort
. Процедура компаратора, используемая qsort
, передается как указатель на функцию. Множество других функций "общего алгоритма" будет принимать аналогичные методы; например возможно, реализация хэш-таблицы может принять вашу хэш-функцию.
Инструментальные средства GUI на языке C и интерфейсы приложений (например, Gnome/Gtk +/Glib) часто принимают указатели на функции как "обратные вызовы" для событий таймера или пользовательского интерфейса. (EG: "вызывать эту функцию всякий раз, когда эта кнопка нажата" или "... по истечении этого таймера" )
Фактически, большинство "ООП-подобных" или "управляемых событиями" кода в C будут принимать указатели на функции по той же причине.
Ответ 7
Вы можете использовать его для передачи обратного вызова функции. Например, вы можете отсортировать массив с помощью qsort()
. Эта функция выполняет функцию сравнения как один из ее аргументов, а это означает, что вы можете использовать свои собственные порядки сортировки:
// All odd numbers are before even numbers
int cmpoddeven(const void *xp, const void *yp) {
int x = *((int*) xp);
int y = *((int*) yp);
if(x == y)
return 0;
if(x % 2 == y % 2) {
return (x < y ? -1 : 1);
if(x % 2 == 1)
return -1;
return 1;
}
int main() {
int array[] = {1, 2, 3, 4, 5};
// calling qsort with cmpoddeven as the comparison function
qsort(array, 5, sizeof(int), &cmpoddeven);
// array == {1, 3, 5, 2, 4};
}
Ответ 8
В случаях MOST это, по сути, способ C для инверсии зависимостей. В статье wiki говорится:
а. Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций. B. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Классический пример qsort
делает это в том смысле, что функция сортировки более высокого уровня не зависит от типа, размера или метода сравнения данных, подлежащих сортировке. Поэтому, если вы qsort()
массив ints, детали sizeof(int)
и ваша реализация сравнения. Абстракция представляет собой массив элементов произвольного размера и функцию, которая сравнивает элементы этого типа.
Смотрите также: Инверсия управления.
Я удивлен, что никто не упомянул pthread_create()
в качестве примера.
Единственное общее использование, о котором я могу думать, не может быть обобщено, поскольку инверсия зависимостей реализует управление потоком, подобранное как тип переключения, для непереключаемых типов данных. Например, если вы когда-либо хотели включить строку, создайте сопоставление массивов отсортированных строковых ключей с указателями функций и выполните двоичный поиск. Это не O (1), как переключатель, но лучше, чем слепо делать strcmp() в большом if-else, пока не найдете совпадение. Но, возможно, не лучше, чем токенизация строки и использование фактического коммутатора.