Puntatore a funzione in C

Jinku Hu 12 ottobre 2023
  1. Usa la notazione void (*func)() per definire il puntatore a funzione in C
  2. Utilizzare la matrice di puntatori di funzione per implementare la funzione di programmazione generica di tipo
Puntatore a funzione in C

Questo articolo introdurrà come utilizzare un puntatore a funzione in C.

Usa la notazione void (*func)() per definire il puntatore a funzione in C

I puntatori a funzione sono ancora un altro costrutto nella programmazione C che implementano funzionalità avanzate come chiamate dinamiche di funzioni, strutture che includono i propri metodi simili alla progettazione orientata agli oggetti, programmazione generica di tipo e così via. La notazione void (*func)(void) dichiara il puntatore alla funzione void che non accetta parametri. Sebbene assegniamo l’indirizzo della funzione printInt, questo richiede un singolo argomento int al puntatore alla funzione di tipo void (*func)(void) nell’esempio seguente. Una volta definito il puntatore a funzione denominato func, può essere chiamato con la solita notazione di chiamata di funzione func(arg) o con l’operatore di dereferenziazione func(arg).

#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);
}

Produzione:

printed from printInt: 10233
printed from printDouble: 11.234000

In alternativa, possiamo definire un nuovo alias di tipo di un puntatore a funzione usando typedef per rendere il codice più leggibile. Notare che diversi tipi di funzioni necessitano di istruzioni typedef separate. Nel seguente esempio di codice, definiamo un puntatore a una funzione void senza argomenti; tuttavia, entrambi gli indirizzi delle funzioni printInt e printDouble sono memorizzati nella variabile di tipo FuncPtr. Si noti che l’indirizzo della funzione specifica può essere preso con l’operatore & esplicito o con l’assegnazione implicita del nome della funzione stessa, come mostrato nel prossimo esempio.

#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);
}

Produzione:

printed from printInt: 10233
printed from printDouble: 11.234000

Utilizzare la matrice di puntatori di funzione per implementare la funzione di programmazione generica di tipo

Come altri oggetti, si può definire un array di puntatori a funzione con la notazione tra parentesi []. Questo array può essere utilizzato per scegliere e chiamare facilmente funzioni specifiche durante il runtime. Si noti che stiamo usando la parola chiave _Generic, che è switch come un’espressione che consente all’utente di scegliere il caso specifico in base alla valutazione del tipo dell’espressione di controllo. Di conseguenza, implementiamo il seguente esempio di codice, dove la corrispondente funzione print viene chiamata in base al tipo di variabile passata nella condizione switch. Si noti che il tipo enum viene utilizzato anche per definire valori costanti per casi diversi.

#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);
}

Produzione:

printed from printInt: 10233
printed from printDouble: 11.234000
Autore: 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

Articolo correlato - C Pointer