Для чего используются указатели на функции и как их использовать? - PullRequest
14 голосов
/ 18 ноября 2009

Я понимаю, что могу использовать указатели для функций.

Может кто-нибудь объяснить, почему их использовать и как? Короткий пример кода был бы очень полезен для меня.

Ответы [ 9 ]

22 голосов
/ 18 ноября 2009

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

Вот пример кода:

typedef void (*fn)(void) FNTYPE;
FNTYPE fn_arr[5];

fn_arr[0] = fun1; // fun1 is previously defined
fn_arr[1] = fun2;
...

void callMyFun(string inp) {
    int idx = decideWhichFun(inp); // returns an int between 0 and 4
    fn_arr[idx]();
}

Но, конечно, обратные вызовы - наиболее распространенное использование. Пример кода ниже:

void doLengthyOperation(string inp, void (*callback)(string status)) {
  // do the lengthy task
  callback("finished");
}

void fnAfterLengthyTask(string status) {
    cout << status << endl;
}

int main() {
    doLengthyOperation(someinput, fnAfterLengthyTask);
}
13 голосов
/ 18 ноября 2009

Одним из довольно распространенных вариантов использования является функция обратного вызова. Например, если вы загружаете что-то из БД, вы можете реализовать свою функцию загрузки, чтобы она сообщала о ходе выполнения функции обратного вызова. Это можно сделать с помощью указателей на функции.

5 голосов
/ 18 ноября 2009

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

3 голосов
/ 18 ноября 2009

Я удивлен, что никто не упомянул "конечные автоматы". Указатели на функции являются очень распространенным способом реализации конечных автоматов для таких задач, как анализ. См. Например: ссылка .

3 голосов
/ 18 ноября 2009

Вы используете указатель на функцию, когда вам нужно дать метод обратного вызова. Одним из классических примеров является регистрация обработчиков сигналов - какая функция будет вызываться, когда ваша программа получит SIGTERM (Ctrl-C)

Вот еще один пример:

// The four arithmetic operations ... one of these functions is selected
// at runtime with a switch or a function pointer
float Plus    (float a, float b) { return a+b; }
float Minus   (float a, float b) { return a-b; }
float Multiply(float a, float b) { return a*b; }
float Divide  (float a, float b) { return a/b; }

// Solution with a switch-statement - <opCode> specifies which operation to execute
void Switch(float a, float b, char opCode)
{
   float result;

   // execute operation
   switch(opCode)
   {
      case '+' : result = Plus     (a, b); break;
      case '-' : result = Minus    (a, b); break;
      case '*' : result = Multiply (a, b); break;
      case '/' : result = Divide   (a, b); break;
   }

   cout << "Switch: 2+5=" << result << endl;         // display result
}  

// Solution with a function pointer - <pt2Func> is a function pointer and points to
// a function which takes two floats and returns a float. The function pointer
// "specifies" which operation shall be executed.
void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float))
{
   float result = pt2Func(a, b);    // call using function pointer

   cout << "Switch replaced by function pointer: 2-5=";  // display result
   cout << result << endl;
}

Подробнее об указателях функций можно узнать здесь http://www.newty.de/fpt/index.html

Если вы более знакомы с объектно-ориентированными языками, вы можете думать об этом как о способе C реализовать шаблон проектирования стратегии .

1 голос
/ 19 ноября 2009

Очень хороший и простой для понимания учебник: http://www.newty.de/fpt/index.html

Надеюсь, это поможет.

1 голос
/ 18 ноября 2009

Давайте сделаем map -подобную функцию для C.

void apply(int *arr, size_t len, int (*func)(int))
{
    for(size_t i = 0; i < len; i++)
        arr[i] = func(arr[i]);
}

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

void apply_enumerated(int *arr, size_t len, int (*func)(size_t, int))
{
    for(size_t i = 0; i < len; i++)
        arr[i] = func(i, arr[i]);
}

Это делает то же самое, но позволяет нашей функции знать, на каком элементе она находится. Мы могли бы использовать это, например:

int cube(int i) { return i * i * i }

void print_array(int *array, size_t len, char *sep)
{
    if(sep == NULL) sep = ", ";
    printf("%d", *array);
    for(size_t i = 1; i < len; i++) printf("%s%d", sep, array[i])
}

#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))

int main(void)
{
    int array[5] = { 1, 2, 3, 4, 5 };
    print_array(array, ARRAY_SIZE(array), NULL);
    apply(array, ARRAY_SIZE(array), cube);
    print_array(array, ARRAY_SIZE(array), NULL);
    return 0;
}

Этот код напечатает:

1, 2, 3, 4, 5
1, 8, 27, 64, 125

Для нашего примера перечисления:

int mult(size_t i, int j) { return i * j }

// print_array and ARRAY_SIZE as before

int main(void)
{
    int array[5] = { 1, 2, 3, 4, 5 };
    print_array(array, ARRAY_SIZE(array), NULL);
    apply_enumerated(array, ARRAY_SIZE(array), mult);
    print_array(array, ARRAY_SIZE(array), NULL);
    return 0;
}

Это печатает:

1, 2, 3, 4, 5
0, 2, 6, 12, 20

В качестве более реального примера строковая библиотека может иметь функцию, которая применяет функцию, которая работает с одиночными символами, ко всем символам в строке. Примером таких функций являются функции стандартной библиотеки toupper() и tolower() в ctype.h - мы могли бы использовать эту функцию string_apply(), чтобы сделать функцию string_toupper() легкой.

0 голосов
/ 19 ноября 2009

Код можно найти в ответе qrdl на руководства по конечным автоматам .

Я использовал вариант его метода, чтобы реализовать меню для ЖК-дисплея, который у меня есть на доске. Очень очень полезно, делает кодирование намного чище и легче для чтения. Использование указателей функций упрощает расширение числа, названий и упорядочение меню и скрывает все эти детали от вызывающей функции более высокого уровня, которая просто видит «эй, я пишу на ЖК-дисплей здесь».

0 голосов
/ 18 ноября 2009

Другое использование для указателей - итерация списков или массивов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...