Zeiger auf Funktionen als Rückgabewerte zu erhalten
Die Wertzuweisung an eine Funktionszeigervariable erfolgt wie bei den Arrays,
so ist auch bei Funktionen der Name allein als Zeiger auf die Funktion festgelegt.
Es wird kein &-Operator angewendet, der Funktionsname ist eine
Adreßkonstante! Beispiel:
int Funkt(int); /* Deklaration der Funktion Funkt() */
int (*ZFunkt) (int); /* Definition der Funktionszeigervariablen */
ZFunkt = Funkt; /* Zuweisung der Adresse der Funktion */
/* als Wert an den Zeiger */
Der Aufruf einer Funktion über einen Funktionszeiger erfolgt durch
Dereferenzieren (mit *) des Zeigers und Angabe der aktuellen Parameterliste.
Zum Beispiel:
Ergebnis = (*ZFunkt)(2*i);
Es stellt sich die Frage, wozu sowas nötig sein sollte. Man kann auf
diese Weise allgemein verwendbare Funktionen programmieren. Angenommen,
sie wollen eine Funktion schreiben, die den Graphen einer beliebigen Funktion
zeichnet. Ohne Funktionszeiger müßte die Funktion jeweils
im Quellcode eingefügt und dann das Programm neu compiliert
und ausgeführt werden. Natürlich kann man sowas auch automatisieren.
Ein Programm öffnet eine Quelldatei, modifiziert sie, startet die
Compilierung und führt schließlich das erzeugte Programm aus.
Das Ganze ist aber recht kompliziert. Einfacher und eleganter ist es da sicher,
an die Grafikfunktion einfach einen Zeiger auf eine beliebige Funktion
zu übergeben.
Ein erstes Beispiel:
#include <stdio.h>
int i;
int Funzel(); /* Deklaration der Funktion */
int (*ZFunkt) (); /* Definition des Funktionszeigers */
int (*druck) (char *format,...); /* ja, das geht :-) */
int main(void)
{
ZFunkt = Funzel; /* Zuweisung der Funktionsadresse */
i = (*ZFunkt)(); /* Funktion ausfuehren */
/* Stimmen die Adressen? */
printf("Adresse Funzel(): %p, Adresse ZFunkt: %p\n", ZFunkt, Funzel);
druck = printf; /* Zuweisung der Funktionsadresse */
/* druck ausfuehren: */
(*druck) ("Sie mal einer da! I war uebrigens: %d\n", i);
return(0);
}
int Funzel()
{
printf("Funzel was here!\n");
return(42);
}
Die Ausgabe sieht etwa so aus:
Funzel was here!
Adresse Funzel(): 0x401120, Adresse ZFunkt: 0x401120
Sie mal einer da! I war uebrigens: 42
Natürlich sind auch Arrays von Funktionspointern möglich, Beispiel:
double (*trig[3])(double), x;
trig[0] = exp;
trig[1] = sin;
trig[2] = cos;
Die Aufrufe wie z. B. sin(x) und trig[1](x) sind dann
äquivalent. Funktionen können so mit einem Index versehen werden.
Funktionspointer und Funktionvariable erlauben es, generische Funktionen
zu schreiben. Als Beispiel diene hier die Bibliotheksfunktion qsort()
aus <stdlib.h>:
void qsort(void *base, size_t nel, size_t width,
int (*compar) (const void *, const void *));
qsort() sortiert ein Array base[0] bis base[nel-1] von
Objekten der Größe width in aufsteigender Reihenfolge.
Die Vergleichsfunktion compar() gibt einen negativen Wert zurück,
wenn ihr erstes Argument kleiner ist als das zweite, Null wenn die Argumente gleich
sind und einen positeven Wert, wenn das zweite größer als das erste ist.
In qsort() werden Vergleiche immer mit der Funktion compar()
vorgenommen:
void qsort(void *base, size_t nel, size_t width,
int (*compar) (const void *, const void *))
{
...
if ((*compar)(base[i], base[j]) < 0)
swap(base, i, j);
...
}
Mit der Wahl einer geeigneten Vergleichsfunktion können also Arrays
beliebigen Typs sortiert werden.
Beispiel:
#include <stdlib.h>
#include <string.h>
int agecompare(const void *i, const void *j); /* Alter vergleichen ... */
int namecompare(const void *i, const void *j); /* Namen vergleichen ... */
typedef struct
{
char *name;
int age;
} person;
int main(void)
{
person a[ARRAYSIZE];
... /* Initialisierungen ... */
/* a nach Alter sortieren */
qsort(a, ARRAYSIZE, sizeof(person), agecompare);
...
/* a nach Namen sortieren */
qsort(a, ARRAYSIZE, sizeof(person), namecompare);
...
}
int agecompare(const void *i, const void *j)
{
int ii,jj;
ii = ((person*)i)->age;
jj = ((person*)j)->age;
if ( ii > jj) return 1;
if ( jj > ii) return -1;
return(0);
}
int namecompare(const void *i, const void *j)
{
char *ii, *jj;
ii = ((person*)i)->name;
jj = ((person*)j)->name;
return strcmp(ii, jj);
}
Programmierbeispiel: Integration nach Simpson
#include <stdio.h>
#include <math.h>
double dkreis (double dX);
double simpson (double dXu, double dXo, double dEps,
double (*f)(double dVal));
/* oberer Halbkreis mit Radius 1 */
/* Flaeche ergibt Pi/2. */
double dkreis (double dX)
{
double dR = 1.;
double dY;
if (dX > 1.) return 0.;
if (dX < -1.) return 0.;
dY = sqrt (dR*dR -dX*dX);
return dY;
}
double simpson(
double dXu; /* untere Grenze */
double dXo; /* obere Grenze */
double dEps; /* Genauigkeit */
double (*f)(double dVal)) /* zu integrierende Funktion */
{
int i;
int nrun = 0; /* Laufkenner */
double dFl = 0.; /* letzter Integralwert */
double dFa = 0.; /* aktueller Integralwert */
double dH; /* Schrittweite */
double dX; /* X-Wert */
double dY; /* Funktionswert */
double dg; /* Gewicht */
double dErr; /* Fehler */
int n = 2; /* Inkrementierung */
dFa = 0.;
/* Schleife über alle Integrationen */
while (1)
{
dFa = 0.;
dH = (dXo -dXu)/(double)n; /* Schrittweite bestimmen */
for (i=0; i<=n; i++)
{
dX = dXu +dH*(double)i;
dY = f (dX);
if (i == 0 || i== n) dg = 1.;
else if (i%2 == 1) dg = 4.;
else if (i%2 == 0) dg = 2.;
else dg = 0.;
dFa += dg*dY;
}
dFa *= dH/3.;
dErr = fabs((dFa -dFl)/dFa);
if (nrun && dErr < dEps) break;
n*=2;
nrun++;
dFl = dFa;
}
return dFa;
}
void main()
{
double dXo = 1.;
double dXu = -1.;
double dEps = 1.e-6;
double dF;
dF = simpson (dXu,dXo,dEps, dkreis );
printf (" Xu: %8.4f, Xo: %8.4f, F: %12.8f Eps: %g\n",dXu,dXo,dF,dEps);
}
|
|