C Language
Funktionspekare
Sök…
Introduktion
Funktionspekare är pekare som pekar på funktioner i stället för datatyper. De kan användas för att tillåta variation i funktionen som ska kallas vid körning.
Syntax
returnType (* namn) (parametrar)
typedef returnType (* namn) (parametrar)
typedef returnType Namn (parametrar);
Namn * namn;typedef returnType Namn (parametrar);
typedef Namn * NamePtr;
Tilldela en funktionspekare
#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;
}
Återvända funktionspekare från en funktion
#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;
}
Bästa metoder
Med typedef
Det kan vara praktiskt att använda en typedef
istället för att deklarera funktionspekaren varje gång för hand.
Syntaxen för att deklarera en typedef
för en funktionspekare är:
typedef returnType (*name)(parameters);
Exempel:
Postera att vi har en funktion, sort
, som förväntar sig att en funktionspekare till en funktion compare
så att:
jämför - En jämför-funktion för två element som ska levereras till en sorteringsfunktion.
"jämför" förväntas returnera 0 om de två elementen bedöms lika, ett positivt värde om det första elementet som passeras är "större" i någon mening än det senare elementet och annars returnerar funktionen ett negativt värde (vilket betyder att det första elementet är "mindre" än det senare).
Utan en typedef
skulle vi skicka en funktionspekare som ett argument till en funktion på följande sätt:
void sort(int (*compare)(const void *elem1, const void *elem2)) {
/* inside of this block, the function is named "compare" */
}
Med en typedef
skulle vi skriva:
typedef int (*compare_func)(const void *, const void *);
och sedan kunde vi ändra funktionssignaturen för sort
till:
void sort(compare_func func) {
/* In this block the function is named "func" */
}
båda definitionerna av sort
skulle acceptera alla funktioner i formen
int compare(const void *arg1, const void *arg2) {
/* Note that the variable names do not have to be "elem1" and "elem2" */
}
Funktionspekare är den enda platsen där du bör inkludera pekareegenskapen av typen, t.ex. försök inte att definiera typer som typedef struct something_struct *something_type
. Detta gäller även för en struktur med medlemmar som inte är tänkta att komma åt direkt av API-anropare, till exempel stdio.h FILE
typen (som som du nu kommer att märka är inte en pekare).
Ta sammanhangspunkter.
En funktionspekare bör nästan alltid ta en tom leverantör som levereras av användaren * som en sammanhangspekare.
Exempel
/* 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);
}
Att använda kontextpekaren innebär att de extra parametrarna inte behöver vara hårdkodade i den funktion som pekas på eller kräver användning av globaler.
Biblioteksfunktionen qsort()
följer inte denna regel och man kan ofta komma undan utan sammanhang för triviella jämförelsefunktioner. Men för allt mer komplicerat blir kontextpekaren väsentlig.
Se även
Introduktion
Precis som char
och int
är en funktion en grundläggande funktion i C. Som sådan kan du förklara en pekare till en: vilket innebär att du kan skicka vilken funktion att ringa till en annan funktion för att hjälpa den att utföra sitt jobb. Om du till exempel hade en graph()
-funktion som visade en graf, kan du överföra vilken funktion som ska graferas till 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)
Användande
Så koden ovan graferar vilken funktion du har gått in i den - så länge den funktionen uppfyller vissa kriterier: nämligen att du passerar en double
in och får en double
ut. Det finns många funktioner som det - sin()
, cos()
, tan()
, exp()
etc. - men det finns många som inte är det, till exempel graph()
själv!
Syntax
Så hur anger du vilka funktioner du kan överföra till graph()
och vilka du inte kan? Det konventionella sättet är att använda en syntax som kanske inte är lätt att läsa eller förstå:
double (*fn)(double); // fn is a pointer-to-function that takes a double and returns one
Problemet ovan är att det finns två saker som försöker definieras samtidigt: funktionens struktur och det faktum att det är en pekare. Så dela de två definitionerna! Men genom att använda typedef
kan en bättre syntax (lättare att läsa och förstå) uppnås.
Mnemonic för att skriva funktionspekare
Alla C-funktioner är i själva verket pekar på en plats i programminnet där någon kod finns. Den huvudsakliga användningen av en funktionspekare är att tillhandahålla en "återuppringning" till andra funktioner (eller för att simulera klasser och objekt).
Syntaxen för en funktion som definieras längre ner på denna sida är:
returnType (* namn) (parametrar)
En mnemonic för att skriva en funktionspekerdefinition är följande procedur:
- Börja med att skriva en normal funktionsdeklaration:
returnType name(parameters)
- Radera in funktionsnamnet med
returnType (*name)(parameters)
:returnType (*name)(parameters)
Grunderna
Precis som du kan ha en pekare till en int , char , float , array / string , struct , etc. - du kan ha en pekare till en funktion.
Om du pekar på pekaren tas funktionens returvärde, funktionens namn och typen av argument / parametrar den får .
Säg att du har följande funktion deklarerad och initierad:
int addInt(int n, int m){
return n+m;
}
Du kan förklara och initiera en pekare till den här funktionen:
int (*functionPtrAdd)(int, int) = addInt; // or &addInt - the & is optional
Om du har en ogiltig funktion kan det se ut så här:
void Print(void){
printf("look ma' - no hands, only pointers!\n");
}
Sedan förklarar pekaren till att det skulle vara:
void (*functionPtrPrint)(void) = Print;
Åtkomst till själva funktionen kräver att du markerar markören:
sum = (*functionPtrAdd)(2, 3); //will assign 5 to sum
(*functionPtrPrint)(); //will print the text in Print function
Som framgår av mer avancerade exempel i det här dokumentet, kan det vara rörigt att förklara en pekare till en funktion om funktionen ges mer än ett par parametrar. Om du har några pekare till funktioner som har identisk "struktur" (samma typ av returvärde och samma typ av parametrar) är det bäst att använda kommandot typedef för att spara en viss typning och för att göra koden tydligare:
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;
}
Du kan också skapa en matris med funktionspekare . Om alla pekare har samma "struktur":
int (*array[2]) (int x, int y); // can hold 2 function pointers
array[0] = Add;
array[1] = Multiply;
Du kan lära dig mer här och här .
Det är också möjligt att definiera en mängd funktionspekare av olika typer, men det kräver casting när du någonsin vill komma åt den specifika funktionen. Du kan lära dig mer här .