Попытка понять указатели на функции в C - PullRequest
4 голосов
/ 26 марта 2012

Я пытаюсь понять функциональные указатели и я теряюсь.Я видел пример сортировки в K & R и несколько других подобных примеров.Моя главная проблема в том, что на самом деле делает компьютер.Я создал очень простую программу, чтобы попытаться увидеть основы.Пожалуйста, смотрите следующее:

#include <stdio.h>

int func0(int*,int*);
int func1(int*,int*);

int main(){

    int i = 1;
    myfunc(34,23,(int(*)(void*,void*))(i==1?func0:func1));//34 and 23 are arbitrary inputs



}

void myfunc(int x, int y, int(*somefunc)(void *, void *)){

    int *xx =&x;
    int *yy=&y;

    printf("%i",somefunc(xx,yy));


}

int func0(int *x, int *y){

    return (*x)*(*y);

}

int func1(int *x, int *y){

    return *x+*y;
} 

Программа либо умножает, либо добавляет два числа в зависимости от некоторой переменной (i в главной функции - вероятно, должно быть аргументом в главной).fun0 умножает два целых числа, а func1 добавляет их.

Я знаю, что этот пример прост, но как передать указатель на функцию предпочтительнее, чем поместить условие в функцию myfunc?т.е. в myfunc есть следующее:

if(i == 1)printf("%i",func0(xx,yy));

else printf("%i",func1(xx,yy));

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

Ответы [ 3 ]

10 голосов
/ 26 марта 2012

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

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

struct foo {
  char * name;
  int magnitude;
  int something;
};

int cmp_foo(const void *_p1, const void *_p2)
{
  p1 = (struct foo*)_p1;
  p2 = (struct foo*)_p2;

  return p1->magnitude - p2->magnitude;
}

struct foo ** foos;
// init 10 foo structures...

qsort(foos, 10, sizeof(foo *), cmp_foo);

Затем массив foos будет отсортирован по полю величины.

Как видите, это позволяет вам использовать qsort для любого типа - вам нужно только предоставить функцию сравнения.

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

2 голосов
/ 26 марта 2012

как передать указатель на функцию предпочтительнее, чем помещать условное выражение внутри функции myfunc

Иногда невозможно поставить условие там: например, если вы пишете алгоритм сортировки, и вы не знаете, что вы сортируете заранее, вы просто не можете поставить условие; указатель на функцию позволяет вам «вставить» часть вычислений в основной алгоритм, не перепрыгивая через обручи.

Что касается механизма, идея проста: весь ваш скомпилированный код находится в памяти программы, и ЦПУ выполняет его, начиная с определенного адреса. Существуют инструкции, позволяющие процессору переходить между адресами, запоминать текущий адрес и переход, вспоминать адрес предыдущего перехода и возвращаться к нему, и так далее. Когда вы вызываете функцию, одна из вещей, которую ЦП должен знать, - это ее адрес в памяти программы. Имя функции представляет этот адрес. Вы можете указать этот адрес напрямую или назначить указатель для косвенного доступа. Это похоже на доступ к значениям через указатель, за исключением того, что в этом случае вы обращаетесь к коду косвенно, а не к данным.

1 голос
/ 26 марта 2012

Прежде всего, вы никогда не сможете типизировать указатель функции в указатель функции другого типа. Это неопределенное поведение в C (C11 6.5.2.2).

Очень важный совет при работе с указателями функций - всегда использовать typedefs.

Итак, ваш код можно / нужно переписать как:

typedef int (*func_t)(int*, int*);

int func0(int*,int*);
int func1(int*,int*);

int main(){

    int i = 1;
    myfunc(34,23, (i==1?func0:func1)); //34 and 23 are arbitrary inputs
}

void myfunc(int x, int y, func_t func){

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

Взять в качестве примера стандартную функцию C bsearch():

void *bsearch (const void *key, 
               const void *base,
               size_t nmemb, 
               size_t size,
               int (*compar)(const void *, const void *));
              );

Это универсальный алгоритм двоичного поиска, который осуществляет поиск в любой форме одномерного массива, содержащего неизвестные типы данных, такие как определяемые пользователем типы. Здесь функция «сравнения» сравнивает два объекта неизвестной природы на равенство, возвращая число, указывающее на это.

"Функция должна возвращать целое число меньше, равно или больше нуля, если ключевой объект считается, соответственно, меньше, чтобы соответствовать или быть больше, чем элемент массива."

Функция написана вызывающей стороной, которая знает природу данных. В информатике это называется «функциональным объектом» или иногда «функтором». Обычно встречается в объектно-ориентированном дизайне.

Пример (псевдокод):

typedef struct  // some user-defined type
{
  int* ptr;
  int  x;
  int  y;
} Something_t;

int compare_Something_t (const void* p1, const void* p2)
{
  const Something_t* s1 = (const Something_t*)p1;
  const Something_t* s2 = (const Something_t*)p2;

  return s1->y - s2->y;  // some user-defined comparison relevant to the object
}

...

Something_t  search_key = { ... };
Something_t  array[] = { ... };
Something_t* result; 

result = bsearch(&search_key,
                 array,
                 sizeof(array) / sizeof(Something_t), // number of objects
                 sizeof(Something_t), // size of one object
                 compare_Something_t // function object
                );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...