C Language
Указатели функций
Поиск…
Вступление
Указатели функций - это указатели, указывающие на функции вместо типов данных. Они могут использоваться для обеспечения изменчивости функции, которая должна быть вызвана во время выполнения.
Синтаксис
returnType (* name) (параметры)
typedef returnType (* name) (параметры)
typedef returnType Name (параметры);
Имя * имя;typedef returnType Name (параметры);
typedef Имя * NamePtr;
Назначение указателя функции
#include <stdio.h>
/* increment: take number, increment it by one, and return it */
int increment(int i)
{
printf("increment %d by 1\n", i);
return i + 1;
}
/* decrement: take number, decrement it by one, and return it */
int decrement(int i)
{
printf("decrement %d by 1\n", i);
return i - 1;
}
int main(void)
{
int num = 0; /* declare number to increment */
int (*fp)(int); /* declare a function pointer */
fp = &increment; /* set function pointer to increment function */
num = (*fp)(num); /* increment num */
num = (*fp)(num); /* increment num a second time */
fp = &decrement; /* set function pointer to decrement function */
num = (*fp)(num); /* decrement num */
printf("num is now: %d\n", num);
return 0;
}
Возвращаемые указатели функций из функции
#include <stdio.h>
enum Op
{
ADD = '+',
SUB = '-',
};
/* add: add a and b, return result */
int add(int a, int b)
{
return a + b;
}
/* sub: subtract b from a, return result */
int sub(int a, int b)
{
return a - b;
}
/* getmath: return the appropriate math function */
int (*getmath(enum Op op))(int,int)
{
switch (op)
{
case ADD:
return &add;
case SUB:
return ⊂
default:
return NULL;
}
}
int main(void)
{
int a, b, c;
int (*fp)(int,int);
fp = getmath(ADD);
a = 1, b = 2;
c = (*fp)(a, b);
printf("%d + %d = %d\n", a, b, c);
return 0;
}
Лучшие практики
Использование typedef
Было бы удобно использовать typedef
вместо объявления указателя функции каждый раз вручную.
Синтаксис объявления typedef
для указателя функции:
typedef returnType (*name)(parameters);
Пример:
Положим, что у нас есть функция, sort
, которая ожидает, что указатель функции на функцию compare
, что:
compare - Функция сравнения для двух элементов, которая должна быть предоставлена функции сортировки.
«сравнение», как ожидается, возвращает 0, если два элемента считаются равными, положительное значение, если первый переданный элемент является «большим» в некотором смысле, чем последний элемент, и в противном случае функция возвращает отрицательное значение (это означает, что первый элемент равен «меньше», чем последний).
Без typedef
мы typedef
бы указатель функции как аргумент функции следующим образом:
void sort(int (*compare)(const void *elem1, const void *elem2)) {
/* inside of this block, the function is named "compare" */
}
С typedef
мы напишем:
typedef int (*compare_func)(const void *, const void *);
и тогда мы могли бы изменить подпись функции sort
:
void sort(compare_func func) {
/* In this block the function is named "func" */
}
оба определения sort
будут принимать любую функцию вида
int compare(const void *arg1, const void *arg2) {
/* Note that the variable names do not have to be "elem1" and "elem2" */
}
Указатели функций - это единственное место, где вы должны указать свойство указателя типа, например, не пытайтесь определить такие типы, как typedef struct something_struct *something_type
. Это относится даже для структуры с членами , которые не предполагается доступ непосредственно API абонентов, например, stdio.h FILE
типа (который , как вы уже заметите это не указатель).
Использование контекстных указателей.
Указатель функции должен почти всегда принимать предоставленный пользователем void * в качестве указателя контекста.
пример
/* function minimiser, details unimportant */
double findminimum( double (*fptr)(double x, double y, void *ctx), void *ctx)
{
...
/* repeatedly make calls like this */
temp = (*fptr)(testx, testy, ctx);
}
/* the function we are minimising, sums two cubics */
double *cubics(double x, double y, void *ctx)
{
double *coeffsx = ctx;
double *coeffsy = coeffx + 4;
return coeffsx[0] * x * x * x + coeffsx[1] * x * x + coeffsx[2] * x + coeffsx[3] +
coeffsy[0] * y * y * y + coeffsy[1] * y * y + coeffsy[2] * y + coeffsy[3];
}
void caller()
{
/* context, the coefficients of the cubics */
double coeffs[8] = {1, 2, 3, 4, 5, 6, 7, 8};
double min;
min = findminimum(cubics, coeffs);
}
Использование указателя контекста означает, что дополнительные параметры не должны быть жестко закодированы в функции, на которую указывает или требуют использования глобальных переменных.
Функция библиотеки qsort()
не соответствует этому правилу, и часто можно избегать контекста для тривиальных функций сравнения. Но для чего-то более сложного, указатель контекста становится существенным.
Смотрите также
Вступление
Точно так же, как char
и int
, функция является фундаментальной особенностью C. Как таковая, вы можете объявить указатель на один: это означает, что вы можете передать какую функцию вызывать другой функции, чтобы помочь ей выполнить свою работу. Например, если у вас есть функция graph()
которая отображает график, вы можете передать какую функцию графику в graph()
.
// A couple of external definitions to make the example clearer
extern unsigned int screenWidth;
extern void plotXY(double x, double y);
// The graph() function.
// Pass in the bounds: the minimum and maximum X and Y that should be plotted.
// Also pass in the actual function to plot.
void graph(double minX, double minY,
double maxX, double maxY,
???? *fn) { // See below for syntax
double stepX = (maxX - minX) / screenWidth;
for (double x=minX; x<maxX; x+=stepX) {
double y = fn(x); // Get y for this x by calling passed-in fn()
if (minY<=y && y<maxY) {
plotXY(x, y); // Plot calculated point
} // if
} for
} // graph(minX, minY, maxX, maxY, fn)
использование
Таким образом, вышеприведенный код будет отображать любую функцию, которую вы передали в нее, - пока эта функция удовлетворяет определенным критериям: а именно, что вы передаете double
код и получаете double
. Существует много таких функций: sin()
, cos()
, tan()
, exp()
и т. Д., Но их много, например, graph()
!
Синтаксис
Итак, как вы определяете, какие функции вы можете передать в graph()
а какие из них вы не можете? Обычным способом является использование синтаксиса, который может быть нелегко прочитать или понять:
double (*fn)(double); // fn is a pointer-to-function that takes a double and returns one
Вышеупомянутая проблема состоит в том, что одновременно можно определить две вещи: структуру функции и тот факт, что это указатель. Итак, разделите два определения! Но, используя typedef
, может быть достигнут лучший синтаксис (легче читать и понимать).
Мнемоника для написания указателей функций
Все функции C находятся в указателях актуальности на месте в памяти программы, где существует некоторый код. Основное использование указателя функции заключается в предоставлении «обратного вызова» для других функций (или для имитации классов и объектов).
Синтаксис функции, определенный ниже на этой странице:
returnType (* name) (параметры)
Мнемоника для написания определения указателя функции - это следующая процедура:
- Начните с написания объявления нормальной функции:
returnType name(parameters)
- Оберните имя функции с помощью синтаксиса указателя:
returnType (*name)(parameters)
основы
Так же, как вы можете иметь указатель на int , char , float , array / string , struct и т. Д. - вы можете иметь указатель на функцию.
Объявление указателя принимает возвращаемое значение функции , имя функции и тип аргументов / параметров, которые она получает .
Предположим, что вы объявили и инициализировали следующую функцию:
int addInt(int n, int m){
return n+m;
}
Вы можете объявить и инициализировать указатель на эту функцию:
int (*functionPtrAdd)(int, int) = addInt; // or &addInt - the & is optional
Если у вас есть функция void, она может выглядеть так:
void Print(void){
printf("look ma' - no hands, only pointers!\n");
}
Тогда объявление указателя на него будет:
void (*functionPtrPrint)(void) = Print;
Доступ к самой функции потребует разыменования указателя:
sum = (*functionPtrAdd)(2, 3); //will assign 5 to sum
(*functionPtrPrint)(); //will print the text in Print function
Как видно из более сложных примеров в этом документе, объявление указателя на функцию может стать беспорядочным, если функции передано более нескольких параметров. Если у вас есть несколько указателей на функции, которые имеют идентичную структуру (тот же тип возвращаемого значения и параметры одного и того же типа), лучше всего использовать команду typedef, чтобы сэкономить вам некоторое написание и сделать код более понятным:
typedef int (*ptrInt)(int, int);
int Add(int i, int j){
return i+j;
}
int Multiply(int i, int j){
return i*j;
}
int main()
{
ptrInt ptr1 = Add;
ptrInt ptr2 = Multiply;
printf("%d\n", (*ptr1)(2,3)); //will print 5
printf("%d\n", (*ptr2)(2,3)); //will print 6
return 0;
}
Вы также можете создать массив указателей функций . Если все указатели имеют одну и ту же «структуру»:
int (*array[2]) (int x, int y); // can hold 2 function pointers
array[0] = Add;
array[1] = Multiply;
Вы можете узнать больше здесь и здесь .
Также возможно определить массив указателей функций разных типов, хотя для этого требуется кастинг, когда вы хотите получить доступ к определенной функции. Вы можете узнать больше здесь .