C++ Tutorial

Anhang I: Funktionszeiger

In einem Funktionszeiger können, wie unschwer zu erraten ist, Adressen von Funktionen abgelegt werden.

Definition eines Funktionszeigers

Sehen wir uns zunächst die Definition eines Funktionszeigers an. Die Definition eines Funktionszeigers gleicht prinzipiell der Deklaration einer Funktion, deren Adresse im Zeiger abgelegt werden soll. Anstelle des Funktionsnamens wird nun aber innerhalb einer Klammer der Name des Funktionszeigers angegeben. Damit kann ein Funktionszeiger auch nur Adressen von Funktionen aufnehmen, die im Returntyp und in der Anzahl und Art der Parameter mit der Zeigerdefinition übereinstimmen.

1: // Zeiger auf Funktionen vom Typ
2: // void Func();
3: void (*pfnFunc1)();
4: // Zeiger auf Funktionen vom Typ
5: // short Func(short, char*);
6: short (*pfnFunc2)(short, char*);

Bilden einer Funktionsadresse

Nachdem der Funktionszeiger definiert ist, kann ihm die Adresse einer entsprechenden Funktion zugewiesen werden. Um die Adresse einer Funktion zu erhalten, wird nur der Name der Funktion angegeben, d.h., es steht hier kein Adressoperator &.

1: // Funktionsdeklarationen
2: void DoSomething();
3: short DoAnything(short, char*);
4: // Zeigerdefinition ohne decltype
5: void (*pfnFunc1)();
6: short (*pfnFunc2)(short, char*);
7: // Adresse der Funktion zuweisen
8: pfnFunc1 = DoSomething;
9: pfnFunc2 = DoAnything;

Steht die Funktionsdeklaration vor der Definition des Funktionszeigers, kann der Funktionszeiger auch mithilfe von decltype() definiert werden, was die Lesbarkeit und Wartbarkeit des Codes deutlich erhöht.

1: // Funktionsdeklarationen
2: void DoSomething();
3: short DoAnything(short, char*);
4: // Zeigerdefinition mit decltype
5: decltype(DoSomething) *pfnFunc1
6: decltype(DoAnything) *pfnFunc2;
7: // Adresse der Funktion zuweisen
8: pfnFunc1 = DoSomething;
9: pfnFunc2 = DoAnything;

Funktionsaufruf über Funktionszeiger

Bleibt zum Schluss die Frage, wie die Funktion aufgerufen wird, deren Adresse im Zeiger abgelegt ist. Wie bekannt sein sollte, erfolgt der Zugriff auf Speicherstellen über Zeiger mit dem Dereferenzierungsoperator *. Und genauso funktioniert der Aufruf von Funktionen über Funktionszeiger, nur muss der dereferenzierte Funktionszeiger jetzt innerhalb einer Klammer stehen. Erhält die aufzurufende Funktion Parameter, sind diese, wie bei einem normalen Funktionsaufruf, in einer weiteren Klammer anzugeben.

1: // Zeigerdefinition
2: void (*pfnFunc1)();
3: short (*pfnFunc2)(short, char*);
4: ...
5: // Funktionsaufrufe
6: (*pfnFunc1)();
7: short nErg = (*pfnFunc2)(var, text);

Beispiel

Das Beispiel demonstriert den Einsatz eines Funktionszeigers anhand einer sehr einfachen Zustandsmaschine. Bei einer Zustandsmaschine wird in Abhängigkeit vom aktuellen Zustand der nachfolgende Zustand bestimmt. D.h., nur der aktuelle Zustand kennt seine möglichen Nachfolge-Zustände.

Im Beispiel besteht die Zustandsmaschine aus den drei 'Zuständen' State1(), State2() und State3(). Diese Zustände werden über den Zeiger pfnState nacheinander aufgerufen. Dazu wird zunächst in main() der Zeiger auf die erste aufzurufende Funktion State1() gesetzt. In der nachfolgenden while-Schleife wird dann so lange die im Zeiger abgelegte Funktion aufgerufen, bis der Zeiger einen nullptr enthält. Welche Funktion als Nächstes in der while-Schleife aufgerufen wird, wird durch die aktuell ausgeführte Funktion festgelegt. So lädt z.B. State1() als nächste auszuführende Funktion die Adresse der Funktion State2() in den Funktionszeiger. Die Funktion State3() veranlasst schließlich den Abbruch der while-Schleife, in dem abhängig von einer Bedingung dem Zeiger ein nullptr zugewiesen wird.

// Beispiel zur Funktionszeiger
// Dateien einbinden
#include <iostream>
#include <print>

// Deklaration der Zustandsfunktionen
void State1(short&);
void State2(short&);
void State3(short&);
// Funktionszeiger definieren
decltype(State1) *pfnState;

int main ()
{
   short counter = 1;
   // Funktionszeiger auf 1. Zustand
   pfnState = State1;
   // Nun alle Zustände durchlaufen
   do
   {
      // Zustandsfunktion aufrufen
      (*pfnState)(counter);
   } while (pfnState != nullptr);
}

// Funktion für Zustand 1
void State1(short& var)
{
   std::println("Zustand 1: Zaehler= {}\n", var);
   var++;
   // Nächsten Zustand im Funktionszeiger ablegen
   pfnState = State2;
}
// Funktion für Zustand 2
void State2(short& var)
{
   std::println("Zustand 2: Zaehler= {}\n", var);
   var++;
   // Nächsten Zustand im Funktionszeiger ablegen
   pfnState = State3;
}
// Funktion für Zustand 3
void State3(short& var)
{
   std::println("Zustand 3: Zaehler= {}", var);
   // Falls übergebener Parameter kleiner 4 Zustand wieder
   // auf 1 setzen sonst kein weiterer Zustand
   if (var < 4)
      pfnState = State1;
   else
      pfnState = nullptr;
   var++;
}

Zustand 1: Zaehler=1 Zustand 2: Zaehler=2 Zustand 3: Zaehler=3 Zustand 1: Zaehler=4 Zustand 2: Zaehler=5 Zustand 3: Zaehler=6


Copyright 2024 © Wolfgang Schröder
E-Mail mit Fragen oder Kommentaren zu dieser Website an: info@cpp-tutor.de
Impressum & Datenschutz