Funktionszeiger in C

Jinku Hu 12 Oktober 2023
  1. Verwendung der void (*func)() Notation zur Definition von Funktionszeigern in C
  2. Funktionszeiger-Array zur Implementierung einer typgenerischen Programmierfunktion verwenden
Funktionszeiger in C

In diesem Artikel wird die Verwendung eines Funktionszeigers in C vorgestellt.

Verwendung der void (*func)() Notation zur Definition von Funktionszeigern in C

Funktionszeiger sind ein weiteres Konstrukt in der C-Programmierung, das fortgeschrittene Funktionen wie den dynamischen Funktionsaufruf, Strukturen, die ihre eigenen Methoden enthalten, ähnlich dem objektorientierten Design, typgenerische Programmierung usw. implementiert. Beachten Sie, dass die Deklaration von Funktionszeigern eine komplizierte Syntax haben kann, nämlich die Notation void (*func)(void) deklariert den Zeiger auf eine void-Funktion, die keine Parameter benötigt. Obwohl wir im folgenden Beispiel dem Funktionszeiger vom Typ void (*func)(void) die Adresse der Funktion printInt zuweisen, die ein einzelnes int-Argument entgegennimmt. Sobald der Funktionszeiger namens func definiert ist, kann er mit der üblichen Funktionsaufruf-Notation func(arg) oder mit dem Dereferenzierungsoperator (*func)(arg) aufgerufen werden.

#include <stdio.h>
#include <stdlib.h>

void printInt(int x) { printf("printed from printInt: %d\n", x); }

int main() {
  int input1 = 10233;

  void (*func)(int) = printInt;

  func(input1);
  (*func)(input1);

  exit(EXIT_SUCCESS);
}

Ausgabe:

printed from printInt: 10233
printed from printDouble: 11.234000

Alternativ können wir mit typedef einen neuen Typ-Alias eines Funktionszeigers definieren, um den Code lesbarer zu machen. Beachten Sie, dass unterschiedliche Funktionstypen separate typedef-Anweisungen benötigen würden. Im folgenden Code-Beispiel definieren wir einen Zeiger auf eine void-Funktion ohne Argumente; trotzdem werden die beiden Funktionsadressen printInt und printDouble in der Variablen vom Typ FuncPtr gespeichert. Es sei darauf hingewiesen, dass die Adresse der jeweiligen Funktion mit dem expliziten &-Operator oder mit der impliziten Zuweisung des Funktionsnamens selbst übernommen werden kann, wie im nächsten Beispiel gezeigt wird.

#include <stdio.h>
#include <stdlib.h>

typedef void (*FuncPtr)();

void printInt(int x) { printf("printed from printInt: %d\n", x); }

void printDouble(double x) { printf("printed from printDouble: %f\n", x); }

int main() {
  int input1 = 10233;
  double input2 = 11.234;

  FuncPtr func1 = printInt;
  FuncPtr func2 = printDouble;

  func1(input1);
  func2(input2);

  exit(EXIT_SUCCESS);
}

Ausgabe:

printed from printInt: 10233
printed from printDouble: 11.234000

Funktionszeiger-Array zur Implementierung einer typgenerischen Programmierfunktion verwenden

Wie bei anderen Objekten auch, kann man ein Array von Funktionszeigern mit der Klammerschreibweise [] definieren. Dieses Array kann verwendet werden, um bestimmte Funktionen zur Laufzeit einfach auszuwählen und aufzurufen. Beachten Sie, dass wir das Schlüsselwort _Generic verwenden, das wie ein Ausdruck switch ist, der es dem Benutzer ermöglicht, den spezifischen Fall auf der Basis der Typauswertung des steuernden Ausdrucks zu wählen. Folglich implementieren wir das folgende Code-Beispiel, in dem die entsprechende print-Funktion auf der Grundlage des Typs der in der switch-Bedingung übergebenen Variablen aufgerufen wird. Beachten Sie, dass der Typ enum auch verwendet wird, um konstante Werte für verschiedene Fälle zu definieren.

#include <stdio.h>
#include <stdlib.h>

enum TYPE { INTEGER, DOUBLE, INVALID };

#define typename(x) \
  _Generic((x), int: INTEGER, double: DOUBLE, default: INVALID)

typedef void (*FuncPtr)();

void printInt(int x) { printf("printed from printInt: %d\n", x); }

void printDouble(double x) { printf("printed from printDouble: %f\n", x); }

int main() {
  int input1 = 10233;
  double input2 = 11.234;

  FuncPtr func_ptrs[] = {printInt, printDouble};

  switch (typename(input1)) {
    case INTEGER:
      func_ptrs[INTEGER](input1);
      break;
    case DOUBLE:
      func_ptrs[DOUBLE](input1);
      break;
    case INVALID:
      printf("No corresponding type found!\n");
    default:
      break;
  }

  switch (typename(input2)) {
    case INTEGER:
      func_ptrs[INTEGER](input2);
      break;
    case DOUBLE:
      func_ptrs[DOUBLE](input2);
      break;
    case INVALID:
      printf("No corresponding type found!\n");
    default:
      break;
  }

  exit(EXIT_SUCCESS);
}

Ausgabe:

printed from printInt: 10233
printed from printDouble: 11.234000
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Verwandter Artikel - C Pointer