Funktionenzeiger ermöglichen einige äußerst interessante,effiziente und elegante Programmiertechniken. Leider werden sie - wohl aufgrund ihrer etwas komplizierten Syntax - in Computerbüchern und Dokumentationen recht stiefmütterlich behandelt und wenn überhaupt, so doch nur recht kurz und oberflächlich angesprochen.
Funktionenzeiger sind Zeiger, d.h. Variablen, die auf die Speicheradresse einer Funktion zeigen. Dazu muß man wissen, daß bei den heutigen Betriebssystemen ein laufendes Programm einen bestimmten Speicherplatz im Hauptspeicher zur Verfügung gestellt bekommt und sich in diesem sowohl der ausführbare compilierte Programmcode, als auch die vom Programm verwendeten Variablen befinden. Eine Funktion im Programmcode ist also, genauso wie z.B. ein Charakter-Feld, zunächst einmal nichts anderes als eine Speicheradresse. Wichtig ist nur, wie man, bzw. der Compiler, den Speicherinhalt, der an der entsprechenden Adresse beginnt, interpretiert.
Normalerweise wird, wenn an einer bestimmten Stelle im Programm eine Funktion DoIt() aufgerufen werden soll, im Quellcode an eben jener bestimmten Stelle der Aufruf von DoIt() statisch eingefügt. Nun kann es jedoch sein, daß zum Zeitpunkt der Programmierung, d.h. zur Compilierzeit, noch gar nicht feststeht, welche Funktion aufgerufen werden soll. Dies könnte z.B. sein, wenn es sich um eine sogenannte Callbackfunktion handelt oder wenn dynamisch zur Laufzeit aus mehreren möglichen Funktionen eine aktuell aufzurufende ausgewählt werden soll. Letzteres ließe sich freilich auch mit einer Case-Anweisung lösen, bei der die jeweiligen Funktionen in den Zweigen der Case-Anweisung aufgerufen werden.
Im Folgenden ist als Beispiel, bei dem natürlich aufgrund der Einfachheit desselben der Einsatz von Funktionenzeigern nicht unbedingt sinnvoll ist, die Aufgabe betrachtet, zwei Argumente mittels einer der vier Grundrechenarten zu verknüpfen. Die Aufgabe wird einmal mittels einer Switch-Anweisung und einmal mittels eines Funktionenzeigers gelöst.
{
}
{
}
Wichtige Anmerkung: Ein Funktionenzeiger zeigt auf eine Funktion, die eine feststehende Signatur, d.h. Rückgabetyp und Übergabeparameter, hat. Dies wird vom Compiler im Rahmen der Typkontrolle überprüft. Daher müssen natürlich, wenn man mittels eines Funktionenzeigers aus mehreren Funktionen eine auswählen will, diese Funktionen alle die gleiche Signatur haben!
Es gibt zwei von ihrer Syntax her unterschiedliche Arten von Funktionenzeigern: Die Zeiger auf normale Funktionen (Standard c) und die Zeiger auf member-functions einer Klasse (object-orientierte Programmierung mit c++).
Definition eines Funktionenzeigers
Da ein Funktionenzeiger nichts anderes als eine Variable ist, muß diese wie üblich definiert werden. Ob dies global oder aber lokal in einer Funktion geschieht ist egal. Im folgenden Beispiel wird eine Variable namens paraFunc definiert, die auf eine Funktion zeigt, die eine float- und zwei bool-Variablen als Übergabeparameter bekommt und ein int zurückgibt.
Zuweisung einer Funktionsadresse
Die Zuweisung einer Funktionsadresse an die Funktionenzeiger-Variable ist einfach. Beim Standard c nehme man einfach den Namen einer passenden und dem Copiler bekannten Funktion, im Falle von c++ bilde man mittels des Adressoperators & die Adresse einer Member-Funktion einer Klasse und weise diese zu:
{
};
Es ist übrigens genauso einfach möglich, den Vergleichsoperator '==' zu verwenden. Im folgenden Beispiel wird geprüft, ob paraFunc momentan die Adresse der Funktion DoIt enthält und im Gleichheitsfalle ein Text ausgegeben.
Aufruf einer Funktion über den Zeiger
In Standard c erfolgt der Aufruf einer Funktion über einen Funktionenzeiger genauso wie der normale statische Aufruf einer Funktion, jedoch wird statt des Funktionsnamens der Name der Funktionenzeiger-Variable verwendet. Bei c++ dagegen ist es wichtig zu bedenken, da es sich um eine member-funktion einer Klasse handelt, daß solche Funktionen nur über eine Instanz der Klasse aufgerufen werden können! Im folgenden Beispiel ist davon ausgegangen worden, daß der Aufruf innerhalb einer (anderen) member-funktion der Klasse TMyClass erfolgt und daher der this-Zeiger zur Verfügung steht.
Eigentlich einfach: Arrays mit Funktionen-Zeigern
Sehr interessant ist das Arbeiten mit Arrays von Funktionenzeigern. Dies bietet die Möglichkeit, eine aufzurufende Funktion einfach über einen Index auszuwählen. Die Syntax erscheint zwar etwas kryptisch, was häufig zu Verwirrung führt, ist aber genau betrachtet halb so wild ;-)
Funktionenzeiger ermöglichen das Konzept der sogenannten Callbackfunktionen. Dazu ein Beispiel mit der bekannten Sortierfunktion qsort von Borland (u.a. Standardbibliothek BC5.02). Die Funktion sortiert die Elemente eines Feldes, welches ihr per void}-Zeiger übergeben wird, nach einer Rangordnung. Das Feld kann Elemente eines beliebigen Typs enthalten; dem Sortierfunktion ist lediglich die Anzahl der Elemente des Feldes und die Größe eines Elementes mitzuteilen. Insofern stellt sich die Frage, auf welche Weise die Rangordnung der einzelnen Elemente der Sortierfunktion bekannt sein sollte: Nun, die Sortierfunktion bekommt den Zeiger auf eine vom Programmierer zu schreibende Vergleichs-Funktion, die für zwei per Zeiger auf void übergebene Elemente die Rangordnung bestimmt und im Rückgabewert codiert. Die Implementierung des Sortieralgorithmus´ ist somit vollkommen von der Notwendigkeit entkoppelt, die zu sortierenden Elemente im Voraus zu kennen. Die Deklaration der Funktion qsort liest sich wie folgt:
Dabei bezeichnet field den Zeiger auf das zu sortierende Feld, nElements die Anzahl der Elemente im selbigen, sizeOfAnElement die Größe eines Elementes in Byte und cmpFunc den Zeiger auf die Vergleichsfunktion. Diese Vergleichsfunktion übernimmt zwei Zeiger auf void und gibt ein int zurück. Die Syntax zur Übernahme eines Funktionenzeigers ist dieselbe wie zur Deklaration/Definition desselben. Siehe dazu auch den Absatz über die Definition von Funktionenzeigern. Im Folgenden ist das Beispiel betrachtet, ein Feld mit float-Zahlen zu sortieren.
{
}
{
}