Zoeken…


Invoering

Functie-aanwijzers zijn verwijzingen naar functies in plaats van gegevenstypen. Ze kunnen worden gebruikt om variabiliteit in de functie die moet worden aangeroepen, tijdens runtime mogelijk te maken.

Syntaxis

  • returnType (* name) (parameters)

  • typedef returnType (* name) (parameters)

  • typedef returnType Naam (parameters);
    Naam naam;

  • typedef returnType Naam (parameters);
    typedef Naam * NaamPtr;

Een functie-aanwijzer toewijzen

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

Functie-aanwijzers teruggeven van een functie

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

Best practices

Typedef gebruiken

Het kan handig zijn om een typedef te gebruiken in plaats van de functie-aanwijzer elke keer met de hand te declareren.

De syntaxis voor het declareren van een typedef voor een functiepointer is:

typedef returnType (*name)(parameters);

Voorbeeld:

Stel vast dat we een functie hebben, sort , die van een functie-aanwijzer een functie compare , zodat:

vergelijken - Een vergelijkingsfunctie voor twee elementen die aan een sorteerfunctie moet worden geleverd.

verwacht wordt dat "vergelijken" 0 retourneert als de twee elementen als gelijk worden beschouwd, een positieve waarde als het doorgegeven eerste element in zekere zin "groter" is dan het laatste element en anders retourneert de functie een negatieve waarde (wat betekent dat het eerste element "minder" dan de laatste).

Zonder een typedef zouden we een functiepointer als argument doorgeven aan een functie op de volgende manier:

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

Met een typedef zouden we schrijven:

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

en dan kunnen we de functiehandtekening van sort in:

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

beide definities van sort zouden elke functie van het formulier accepteren

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

Functie-aanwijzers zijn de enige plaats waar u de pointer-eigenschap van het type moet opnemen, probeer bijvoorbeeld niet om types zoals typedef struct something_struct *something_type te definiëren. Dit is zelfs van toepassing op een structuur met leden die niet rechtstreeks door API-bellers worden benaderd, bijvoorbeeld het type stdio.h FILE (dat zoals u nu zult zien geen pointer is).

Contextwijzers nemen.

Een functiepointer moet bijna altijd een door de gebruiker geleverde leegte * als contextpointer nemen.

Voorbeeld

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

Het gebruik van de contextpointer betekent dat de extra parameters niet hard hoeven te worden gecodeerd in de functie waarnaar wordt verwezen, of dat de gebruik-globals nodig zijn.

De bibliotheekfunctie qsort() volgt deze regel niet en men kan vaak wegkomen zonder context voor triviale vergelijkingsfuncties. Maar voor iets ingewikkelder wordt de contextwijzer essentieel.


Zie ook

Functies wijzers

Invoering

Net als char en int , is een functie een fundamenteel kenmerk van C. Als zodanig kun je een aanwijzer naar een verklaren: wat betekent dat je kunt doorgeven welke functie je aan een andere functie kunt gebruiken om het te helpen zijn werk te doen. Als u bijvoorbeeld een functie graph() had die een graph() weergeeft, kunt u deze functie doorgeven aan 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)

Gebruik

Dus de bovenstaande code geeft een grafiek van elke functie die u erin hebt ingevoerd - zolang die functie aan bepaalde criteria voldoet: namelijk dat u een double invoer geeft en een double invoer krijgt. Er zijn veel van dergelijke functies - sin() , cos() , tan() , exp() enz.) Maar er zijn er veel die dat niet zijn, zoals graph() zelf!

Syntaxis

Dus hoe specificeer je welke functies je in graph() kunt doorgeven en welke niet? De conventionele manier is om een syntaxis te gebruiken die misschien niet gemakkelijk te lezen of te begrijpen is:

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

Het probleem hierboven is dat er twee dingen tegelijkertijd proberen te worden gedefinieerd: de structuur van de functie en het feit dat het een aanwijzer is. Deel de twee definities dus! Maar door typedef , kan een betere syntax (beter leesbaar en begrijpelijk) worden bereikt.

Mnemonic voor het schrijven van functie-aanwijzingen

Alle C-functies wijzen in werkelijkheid naar een plek in het programmageheugen waar enige code bestaat. Het belangrijkste gebruik van een functiepointer is om een "callback" naar andere functies te bieden (of om klassen en objecten te simuleren).

De syntaxis van een functie, zoals verderop op deze pagina gedefinieerd, is:

returnType (* name) (parameters)

Een ezelsbruggetje voor het schrijven van een functiepointerdefinitie is de volgende procedure:

  1. Begin met het schrijven van een normale functieverklaring: returnType name(parameters)
  2. Omloop de functienaam met pointer syntax: returnType (*name)(parameters)

Basics

Net zoals je een pointer naar een int , char , float , array / string , struct , etc. kunt hebben, kun je een pointer naar een functie hebben.

Het declareren van de aanwijzer neemt de retourwaarde van de functie , de naam van de functie en het type argumenten / parameters dat het ontvangt .

Stel dat u de volgende functie hebt gedeclareerd en geïnitialiseerd:

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

U kunt een aanwijzer voor deze functie declareren en initialiseren:

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

Als u een ongeldige functie hebt, kan deze er zo uitzien:

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

Dan zou de wijzer erop wijzen:

void (*functionPtrPrint)(void) = Print;

Voor toegang tot de functie zelf zou de verwijzing naar de aanwijzer moeten worden verwijderd:

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

Zoals te zien is in meer geavanceerde voorbeelden in dit document, kan het declareren van een aanwijzer naar een functie rommelig worden als de functie meer dan enkele parameters wordt doorgegeven. Als u een paar aanwijzingen hebt voor functies met een identieke "structuur" (hetzelfde type retourwaarde en hetzelfde type parameters), kunt u het beste het typedef- commando gebruiken om u te typen en om de code duidelijker te maken:

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

U kunt ook een array met functie-aanwijzers maken . Als alle verwijzingen dezelfde "structuur" hebben:

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

Je kunt hier en hier meer leren.

Het is ook mogelijk om een reeks functie-pointers van verschillende typen te definiëren, hoewel dat casten vereist wanneer u toegang wilt krijgen tot de specifieke functie. Je kunt hier meer leren.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow