Поиск…


Вступление

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

Синтаксис

  • 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 &sub;
        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) (параметры)

Мнемоника для написания определения указателя функции - это следующая процедура:

  1. Начните с написания объявления нормальной функции: returnType name(parameters)
  2. Оберните имя функции с помощью синтаксиса указателя: 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;

Вы можете узнать больше здесь и здесь .

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



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow