Suche…


Einführung

Funktionszeiger sind Zeiger, die auf Funktionen statt auf Datentypen verweisen. Sie können verwendet werden, um Variabilität in der aufzurufenden Funktion zur Laufzeit zu ermöglichen.

Syntax

  • returnType (* name) (Parameter)

  • typedef returnType (* name) (Parameter)

  • typedef returnType Name (Parameter);
    Name Name;

  • typedef returnType Name (Parameter);
    typedef Name * NamePtr;

Funktionszeiger zuweisen

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

Funktionszeiger von einer Funktion zurückgeben

#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

Mit typedef

Es kann nützlich sein, ein typedef anstatt den Funktionszeiger jedes Mal von Hand zu deklarieren.

Die Syntax zum Deklarieren eines typedef für einen Funktionszeiger lautet:

typedef returnType (*name)(parameters);

Beispiel:

Positiv, dass wir eine Funktion, sort , haben, die einen Funktionszeiger auf einen Funktionsvergleich erwartet compare so dass:

compare - Eine Vergleichsfunktion für zwei Elemente, die einer Sortierfunktion zugeführt werden soll.

Es wird erwartet, dass "compare" 0 zurückgibt, wenn die beiden Elemente als gleich betrachtet werden, ein positiver Wert, wenn das erste übergebene Element in gewissem Sinne "größer" ist als das letzte Element, und ansonsten gibt die Funktion einen negativen Wert zurück (was bedeutet, dass das erste Element ist.) "weniger" als das letztere).

Ohne ein typedef würden wir einen Funktionszeiger auf folgende Weise als Argument an eine Funktion übergeben:

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

Mit einem typedef wir schreiben:

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

und dann könnten wir die Funktionssignatur von sort in ändern:

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

beide sort würden jede Funktion des Formulars akzeptieren

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

Funktionszeiger sind die einzige Stelle, an der Sie die Zeiger-Eigenschaft des Typs typedef struct something_struct *something_type Versuchen Sie nicht, Typen wie typedef struct something_struct *something_type zu definieren. Dies gilt auch für eine Struktur mit Mitgliedern, auf die nicht direkt von API-Aufrufern zugegriffen werden soll, z. B. der Typ stdio.h FILE (der, wie Sie jetzt feststellen werden, kein Zeiger ist).

Kontext-Zeiger nehmen

Ein Funktionszeiger sollte fast immer eine vom Benutzer angegebene Leere * als Kontextzeiger verwenden.

Beispiel

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

Die Verwendung des Kontextzeigers bedeutet, dass die zusätzlichen Parameter nicht fest in der Funktion, auf die gezeigt wird, codiert werden müssen, oder dass sie die Verwendung von Globalen erfordern.

Die Bibliotheksfunktion qsort() folgt dieser Regel nicht und für triviale Vergleichsfunktionen kann man oft ohne Kontext auskommen. Für etwas komplizierter wird jedoch der Kontextzeiger wesentlich.


Siehe auch

Funktionszeiger

Einführung

Genau wie char und int ist eine Funktion ein grundlegendes Merkmal von C. Als solches können Sie einen Zeiger auf einen deklarieren: Dies bedeutet, dass Sie die zu übergebende Funktion an eine andere Funktion übergeben können, um ihre Aufgabe zu erfüllen. Wenn Sie beispielsweise über eine graph() Funktion verfügen, die eine Grafik anzeigt, können Sie die zu graphierende Funktion an 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)

Verwendungszweck

Der obige Code zeigt also die von Ihnen übergebene Funktion an - sofern diese Funktion bestimmte Kriterien erfüllt: nämlich, dass Sie ein double und ein double . Es gibt viele Funktionen wie sin() , cos() , tan() , exp() usw. - aber es gibt viele, die es nicht gibt, wie zB graph() selbst!

Syntax

Wie legen Sie also fest, welche Funktionen Sie in graph() und welche nicht. Die herkömmliche Methode ist die Verwendung einer Syntax, die möglicherweise nicht leicht zu lesen oder zu verstehen ist:

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

Das obige Problem besteht darin, dass zwei Dinge gleichzeitig versucht werden: die Struktur der Funktion und die Tatsache, dass es sich um einen Zeiger handelt. Teilen Sie also die beiden Definitionen auf! Durch die Verwendung von typedef kann jedoch eine bessere Syntax (besser lesbar und verständlicher) erreicht werden.

Mnemonik zum Schreiben von Funktionszeigern

Alle C-Funktionen sind tatsächlich Zeiger auf eine Stelle im Programmspeicher, an der Code vorhanden ist. Ein Funktionszeiger dient hauptsächlich dazu, anderen Funktionen einen "Rückruf" zu geben (oder Klassen und Objekte zu simulieren).

Die Syntax einer Funktion, wie weiter unten auf dieser Seite definiert, lautet:

returnType (* name) (Parameter)

Eine Mnemonik zum Schreiben einer Funktionszeigerdefinition ist die folgende Prozedur:

  1. Beginnen Sie mit dem Schreiben einer normalen Funktionsdeklaration: returnType name(parameters)
  2. Umbrechen des Funktionsnamens mit der returnType (*name)(parameters) : returnType (*name)(parameters)

Grundlagen

So wie Sie einen Zeiger auf int , char , float , array / string , struct usw. haben können, können Sie auch einen Zeiger auf eine Funktion haben.

Beim Deklarieren des Zeigers werden der Rückgabewert der Funktion , der Name der Funktion und der Typ der empfangenen Argumente / Parameter verwendet .

Angenommen, Sie haben die folgende Funktion deklariert und initialisiert:

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

Sie können einen Zeiger auf diese Funktion deklarieren und initialisieren:

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

Wenn Sie eine Void-Funktion haben, könnte dies folgendermaßen aussehen:

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

Dann würde der Zeiger darauf deklarieren:

void (*functionPtrPrint)(void) = Print;

Für den Zugriff auf die Funktion selbst muss der Zeiger dereferenziert werden:

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

Wie in den fortgeschritteneren Beispielen in diesem Dokument zu sehen ist, kann das Deklarieren eines Zeigers auf eine Funktion unübersichtlich werden, wenn der Funktion mehr als ein paar Parameter übergeben werden. Wenn Sie einige Zeiger auf Funktionen mit identischer "Struktur" haben (gleiche Art von Rückgabewert und gleiche Art von Parametern), empfiehlt es sich, den typedef- Befehl zu verwenden, um Ihnen die Eingabe zu ersparen und den Code deutlicher zu machen:

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

Sie können auch ein Array von Funktionszeigern erstellen. Wenn alle Zeiger dieselbe "Struktur" haben:

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

Hier und hier erfahren Sie mehr.

Es ist auch möglich, ein Array von Funktionszeigern verschiedener Typen zu definieren. Dies erfordert jedoch ein Casting, wenn Sie auf die jeweilige Funktion zugreifen möchten. Sie können erfahren Sie mehr hier .



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow