- I puntatori a funzione in C -
 
COSA SERVE PER QUESTO TUTORIAL
Download | Chiedi sul FORUM | Glossario cognizioni basiche di C e dell'uso dei puntatori
Cosa sono e come si usano i puntatori a funzione

PUNTATORI A FUNZIONI COME VARIABILI
Come definire puntatori a funzione con typedef.

Chiunque abbia una minima esperienza di C conosce i puntatori e il loro utilizzo, ma spesso vengono dimenticati i puntatori a funzione, soprattutto per via della loro complessità sintattica, vediamo cosa sono, a cosa servono e come usarli.
Una funzione è una porzione di codice che una volta compilata viene inserita nell'eseguibile e quindi caricata in memoria al momento dell'esecuzione, essa quindi avrà un indirizzo come qualsiasi altra variabile:


#include <stdio.h>

int main(int argc, char *argv[]) {
    
    printf("%p\n", main);
  
    return 0;
}

Questo codice produrrà un numero che sarà l'indirizzo della funzione main stessa. Si badi che se dopo main si fossero messe le parentesi sarebbe stata una chiamata alla funzione, mentre invece mettendo solo il nome si intende passare come parametro la funzione stessa e quindi il suo indirizzo. Infatti sarebbe stato equivalente scrivere:

printf("%p\n", &main);

Tuttavia non è possibile scrivere sull'area di memoria in condizioni normali e pertanto il seguente codice porterà ad un crash:


#include <stdio.h>

int main(int argc, char *argv[]) {
    /* Punta ad una funzione e non ad un intero in realtà */
    int *indirizzoMain = (int*) main;
    
    printf("%p\n", indirizzoMain);
    
    indirizzoMain[0] = 312;
    
    return 0;
}


Ora abbiamo memorizzato il puntatore alla funzione come un puntatore ad un intero, ma questo è profondamente errato, infatti l'area di memoria contiene una funzione e non un numero intero, pertanto ci sono altri metodi per definire il tipo di un puntatore a funzione. Vediamo un caso semplificato:


#include <stdio.h>

int somma(int a, int b) {
    return a + b;
}

int main(int argc, char *argv[]) {
    int (*funzioneSomma)(int, int);
    funzioneSomma = somma;
    
    printf("%p\n", funzioneSomma);
    
    return 0;
}

In questo caso l'indirizzo che verrà stampato sarà quello della funzione somma, ma vediamo come abbiamo dichiarato il nostro puntatore a funzione, funzioneSomma: esso è del tutto simile al prototipo (o dichiarazione) della funzione somma, a parte per il fatto che al posto del nome della funzione abbiamo racchiuso tra parentesi il nome della variabile che intendiamo dichiarare, preceduto da un asterisco, ad indicare che è un puntatore. Si noti che i nomi degli argomenti possono essere tranquillamente omessi, in quanto ciò che stiamo dicendo al compilatore è: dichiara un variabile di tipo puntatore ad una funzione che riceve come parametri due interi e ne restituisce un altro. Per il resto possiamo usare il nostro puntatore funzioneSomma come un qualunque altro puntatore.
La sintassi come si può vedere è piuttosto ostica e pertanto se dobbiamo usare più volte uno stesso tipo di puntatore a funzione possiamo servirci di un typedef, immaginiamo ad esempio di avere due funzioni, somma e differenza, e di voler creare un tipo per puntatori ad entrambi chiamato operatoreBinario:


#include <stdio.h>

int somma(int a, int b) {
    return a + b;
}

int differenza(int a, int b) {
    return b - a;
}

typedef int (*operatoreBinario)(int, int);

int main(int argc, char *argv[]) {
    operatoreBinario operatore;
	
    operatore = somma;
    printf("%p\n", operatore);
	
    operatore = differenza;
    printf("%p\n", operatore);
    
    return 0;
}

Come si può vedere il typedef funziona alla stessa maniera della dichiarazione della variabile.
Ovviamente dato un puntatore a funzione è possibile chiamare la funzione in questione:


int main(int argc, char *argv[]) {
    operatoreBinario operatore;
    int a = 3, b = 4;
	
    operatore = somma;
    printf("%p %d\n", operatore, operatore(a, b));
	
    operatore = differenza;
    printf("%p %d\n", operatore, (*operatore)(a, b));
    
    return 0;
}

Si noti che la prima sintassi è equivalente alla seconda ma abbreviata.
Per chiarire le idee vediamo come possono essere definiti i puntatori di funzioni che hanno parametri o valori restituiti di tipo differente:


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

void *funzione1(char carattere) {
    return (void*) NULL;
}
typedef void* (*puntatoreFunzione1)(char);

char *funzione2(int *puntatoreAIntero) {
    return (char *) NULL;
}
typedef char* (*puntatoreFunzione2)(int *);

void****** funzione3(int parametro, ...) {
    return NULL;
}
typedef void****** (*puntatoreFunzione3)(int, ...);

RESTITUIRE E PASSARE COME PARAMETRO UN PUNTATORE A FUNZIONE
Sintassi alternative per ricevere e restituire un puntatore a funzione da una funzione. 

Per passare come argomento o far restituire ad una funzione un puntatore a funzione ci sono due vie: la prima semplice e la seconda molto meno leggibile. La prima consiste semplicemente nel servirsi del typedef:


typedef int (*operatoreBinario)(int, int);

operatoreBinario scegli(char simbolo) {
    switch (simbolo) {
        case '+':
            return somma;
            break;
        case '-':
            return differenza;
            break;
        default:
            return somma;
    }
}

La seconda invece è come segue:


int (*scegli(char simbolo))(int, int) {
    switch (simbolo) {
        case '+':
            return somma;
            break;
        case '-':
            return differenza;
            break;
        default:
            return somma;
    }
}

Stesso discorso vale per passare come parametro un puntatore a funzione:


typedef int (*operatoreBinario)(int, int);

/*
int eseguiOperazione(int (*operazione)(int, int), int a, int b) {
*/
int eseguiOperazione(operatoreBinario operazione, int a, int b) {
    return operazione(a, b);
}

 

<< INDIETRO by VeNoM00