Szukaj…


Wprowadzenie

Wskaźniki funkcji to wskaźniki, które wskazują funkcje zamiast typów danych. Można ich użyć, aby umożliwić zmienność funkcji, która ma zostać wywołana, w czasie wykonywania.

Składnia

  • returnType (* nazwa) (parametry)

  • typedef returnType (* name) (parametry)

  • typedef returnType Nazwa (parametry);
    Nazwa nazwa;

  • typedef returnType Nazwa (parametry);
    typedef Nazwa * NamePtr;

Przypisywanie wskaźnika funkcji

#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;
}

Zwracanie wskaźników funkcji z funkcji

#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;
}

Najlepsze praktyki

Używając typedef

Przydatne może być użycie typedef zamiast deklarowania wskaźnika funkcji za każdym razem ręcznie.

Składnia do deklarowania typedef dla wskaźnika funkcji jest następująca:

typedef returnType (*name)(parameters);

Przykład:

Przekonaj się, że mamy funkcję sort , która oczekuje wskaźnika funkcji do funkcji compare tak że:

porównaj - funkcja porównania dwóch elementów, która ma być dostarczona do funkcji sortowania.

Oczekuje się, że „porównaj” zwróci 0, jeśli dwa elementy zostaną uznane za równe, wartość dodatnia, jeśli pierwszy przekazany element jest w pewnym sensie „większy” niż drugi element, w przeciwnym razie funkcja zwraca wartość ujemną (co oznacza, że pierwszy element jest „mniejszy” niż ten drugi).

Bez typedef przekazalibyśmy wskaźnik funkcji jako argument do funkcji w następujący sposób:

void sort(int (*compare)(const void *elem1, const void *elem2)) { 
    /* inside of this block, the function is named "compare" */
}

Z typedef napisalibyśmy:

typedef int (*compare_func)(const void *, const void *);

a następnie moglibyśmy zmienić podpis funkcji sort na:

void sort(compare_func func) { 
    /* In this block the function is named "func" */
}

obie definicje sort zaakceptowałyby dowolną funkcję formularza

int compare(const void *arg1, const void *arg2) {
    /* Note that the variable names do not have to be "elem1" and "elem2" */
}

Wskaźniki funkcji są jedynym miejscem, w którym należy uwzględnić właściwość wskaźnika typu, np. Nie próbuj definiować typów takich jak typedef struct something_struct *something_type . Dotyczy to nawet struktury z elementami, do których nie mają bezpośredniego dostępu osoby wywołujące API, na przykład typ FILE stdio.h (który jak teraz zauważysz, nie jest wskaźnikiem).

Biorąc wskaźniki kontekstu.

Wskaźnik funkcji powinien prawie zawsze przyjmować podaną przez użytkownika wartość void * jako wskaźnik kontekstowy.

Przykład

/* 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);       
}

Użycie wskaźnika kontekstu oznacza, że dodatkowe parametry nie muszą być zakodowane na stałe w funkcji, na którą wskazują, ani nie wymagają użycia globałów.

Funkcja biblioteczna qsort() nie przestrzega tej reguły i często można uciec bez kontekstu dla trywialnych funkcji porównawczych. Jednak w przypadku bardziej skomplikowanych wskaźników kontekstowych staje się niezbędny.


Zobacz też

Wskaźniki funkcji

Wprowadzenie

Podobnie jak char i int , funkcja jest podstawową cechą C. W związku z tym możesz zadeklarować wskaźnik na jeden: co oznacza, że możesz przekazać tę funkcję, aby wywołać inną funkcję, aby pomóc jej wykonać swoją pracę. Na przykład, jeśli miałeś funkcję graph() która wyświetlała wykres, możesz przekazać tę funkcję do wykresu do 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)

Stosowanie

Tak więc powyższy kod wyświetli wykres dowolnej funkcji, którą do niego przekazałeś - o ile funkcja ta spełnia określone kryteria: mianowicie, że przekazujesz double wejście i dostajesz double wyjście. Jest wiele takich funkcji - sin() , cos() , tan() , exp() itd. - ale jest wiele takich, takich jak sam graph() !

Składnia

Jak więc określić, które funkcje można przekazać do graph() a które nie? Konwencjonalny sposób polega na użyciu składni, która może nie być łatwa do odczytania lub zrozumienia:

double (*fn)(double); // fn is a pointer-to-function that takes a double and returns one

Powyższy problem polega na tym, że jednocześnie próbujemy zdefiniować dwie rzeczy: strukturę funkcji oraz fakt, że jest to wskaźnik. Podziel dwie definicje! Ale dzięki typedef można uzyskać lepszą składnię (łatwiejszą do odczytania i zrozumienia).

Mnemoniczny do pisania wskaźników funkcji

Wszystkie funkcje C są w rzeczywistości wskaźnikami do miejsca w pamięci programu, w którym istnieje jakiś kod. Głównym zastosowaniem wskaźnika funkcji jest zapewnienie „wywołania zwrotnego” innych funkcji (lub symulacja klas i obiektów).

Składnia funkcji, jak zdefiniowano w dalszej części tej strony, to:

returnType (* nazwa) (parametry)

Mnemonik do pisania definicji wskaźnika funkcji to następująca procedura:

  1. Zacznij od napisania normalnej deklaracji funkcji: returnType name(parameters)
  2. Zawiń nazwę funkcji składnią wskaźnika: returnType (*name)(parameters)

Podstawy

Tak jak możesz mieć wskaźnik do int , char , float , array / string , struct itp. - możesz mieć wskaźnik do funkcji.

Zadeklarowanie wskaźnika wymaga zwrócenia wartości funkcji , nazwy funkcji i rodzaju otrzymywanych argumentów / parametrów .

Załóżmy, że zadeklarowano i zainicjowano następującą funkcję:

int addInt(int n, int m){
    return n+m;
}

Możesz zadeklarować i zainicjować wskaźnik do tej funkcji:

int (*functionPtrAdd)(int, int) = addInt; // or &addInt - the & is optional

Jeśli masz funkcję void, może to wyglądać następująco:

void Print(void){
    printf("look ma' - no hands, only pointers!\n");
}

W takim przypadku zadeklarowanie wskaźnika to:

void (*functionPtrPrint)(void) = Print;

Dostęp do samej funkcji wymagałby dereferencji wskaźnika:

sum = (*functionPtrAdd)(2, 3); //will assign 5 to sum
(*functionPtrPrint)(); //will print the text in Print function

Jak widać w bardziej zaawansowanych przykładach w tym dokumencie, zadeklarowanie wskaźnika do funkcji może być nieuporządkowane, jeśli funkcja przejdzie więcej niż kilka parametrów. Jeśli masz kilka wskaźników do funkcji, które mają identyczną „strukturę” (ten sam typ wartości zwracanej i ten sam typ parametrów), najlepiej użyć polecenia typedef, aby zaoszczędzić trochę pisania i aby kod był bardziej przejrzysty:

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;
}

Możesz także utworzyć tablicę wskaźników funkcji . Jeśli wszystkie wskaźniki mają tę samą „strukturę”:

int (*array[2]) (int x, int y); // can hold 2 function pointers
array[0] = Add;
array[1] = Multiply;

Możesz dowiedzieć się więcej tutaj i tutaj .

Możliwe jest również zdefiniowanie tablicy wskaźników funkcji różnych typów, choć wymagałoby to rzutowania, gdy tylko chcesz uzyskać dostęp do konkretnej funkcji. Możesz dowiedzieć się więcej tutaj .



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow