когда мы используем указатели на функции - PullRequest
1 голос
/ 07 августа 2010

Я пытаюсь понять существующий код.

Когда мы на самом деле используем указатели на функции?особенно как показано ниже.

struct xx

{
  char *a;

  (*func)(char *a, void *b);

  void *b;

}


struct xx ppp[] = { };

, затем проверьте sizeof (ppp) / sizeof (* ppp);

когда мы пойдем с таким подходом?

Ответы [ 3 ]

6 голосов
/ 07 августа 2010

sizeof array / sizeof *array - это способ узнать, сколько элементов в массиве.(Обратите внимание, что это должен быть массив, а не указатель.) Я не уверен, как это связано с вопросом о вашем указателе функции.

Указатели на функцию используются для хранения ссылки на функцию, чтобы она моглаПозвонил позже.Главное, чтобы указатель на функцию не всегда указывал на одну и ту же функцию.(Если это так, вы можете просто обратиться к функции по имени.)

Вот пример, основанный на вашем коде (хотя я мог бы предоставить лучший, если бы знал, что должен делать ваш код.

char *s1 = "String one";
char *s2 = "String two";

void f(char *a, void *b) {
    /* Do something with a and b */
}
void g(char *a, void *b) {
    /* Do something else with a and b */
}

struct xx {
    char *a;
    void (*func)(char *a, void *b);
    void *b;
}

struct xx ppp[] = { {s1, f, NULL}, {s2, g, NULL} };

int main(int argc, char **argv) {
    for (int i = 0; i < (sizeof ppp / sizeof *ppp); i++) {
        ppp[i].func(ppp[i].a, ppp[i].b);
    }
}
3 голосов
/ 07 августа 2010

Есть два основных использования (которые я знаю) для указателей на функции в C.

1. Callbacks

У вас есть какая-то управляемая событиями инфраструктура (GUI - один из самых простых примеров), и программа хочет реагировать на события по мере их возникновения. Теперь вы можете сделать это с помощью насоса событий, например

while (event *e = get_one_event()) {
  switch (e->type) {
    case EVT_CLICK:
      ...
  }
}

но через некоторое время это утомляет. Другой основной альтернативой являются обратные вызовы. Программа определяет набор функций для обработки различных событий, а затем регистрирует их в библиотеке: «когда происходит событие X, вызовите функцию Y» - поэтому, конечно, библиотека получит указатель на функцию и вызовет ее в соответствующее время.

2. Объекты (таблицы функций / таблицы)

Если вы сделали ОО на большинстве других языков, вам будет довольно легко представить это. Представьте объект как структуру, которая содержит его члены, а затем группу указателей на функции (или, что более вероятно, его члены и указатель на другую структуру, представляющую его класс, который содержит группу указателей на функции). Указатели на функции в таблице являются методами объекта. GLib / GObject - большой пользователь этой техники, как и ядро ​​Linux (struct file_operations, struct device_driver, struct bus_type и многие другие). Это позволяет нам иметь произвольное количество объектов с различным поведением, не увеличивая количество кода.

2 голосов
/ 07 августа 2010

Когда мы действительно пойдем за указателями на функции?особенно, как показано ниже.

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

Структура кнопки может содержать указатель на функцию и контекст:

typedef struct {
    void (*press_button) (void *context);
    void *context;
    /* Here some other stuff */
} Button;

Когда пользователь нажимает кнопку, событие выглядит примерно так:

void event_click (Button *b)
{
    b->press_button(b->context);
}

Смысл в этом заключается в том, что вы можете всегда использовать одну и ту же структуру для каждой кнопки:

Button * create_button (void (*callback) (void *), void *context, /* other params */
{
    Button *ret = malloc(sizeof(Button));
    if (ret != NULL) {
        ret->callback = callback;
        ret->context = context;
        /* Assign other params */
    }
    ...
    return ret;
}

Так что, когда вы строите свой набор инструментов, вы, вероятно, делаете что-то вроде

Button * toolbox[N];
toolbox[0] = create_button(function1, (void *)data, ...);
toolbox[1] = create_button(function2, (void *)something, ...);
...
toolbox[N-1] = create_button(functionN, (void *)something_else, ...);

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

Примечание : Этот метод великолепен, но если вы работаете с C ++, вы можете предпочесть использование объектной ориентациии заменить обратные вызовы производными от абстрактных классов.При этом вам также не нужно переносить контекст, поскольку класс сделает это за вас.

Редактировать в ответ на первый комментарий :

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

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

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

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