Puntatori a funzioni Indirizzo di una funzione Definizione di variabili
Transcript
Puntatori a funzioni Indirizzo di una funzione Definizione di variabili
2 Indirizzo di una funzione Puntatori a funzioni Ver. 2.4 Si può chiamare una funzione utilizzando l’indirizzo di memoria dal quale inizia il codice eseguibile della funzione stessa L’indirizzo di memoria di una funzione può essere assegnato ad una variabile puntatore, memorizzato in un vettore, passato a una funzione, restituito da una funzione © 2010 - Claudio Fornaro - Corso di programmazione in C 3 Definizione di variabili tipo (*nome)(parametri ); definisce la variabile nome come puntatore a una funzione che restituisce un valore del tipo indicato e richiede i parametri indicati Le parentesi intorno al nome sono necessarie altrimenti si ha: tipo *nome(parametri ); e questa costituisce il prototipo di una funzione che restituisce un puntatore del tipo indicato e richiede i parametri indicati 4 Definizione di variabili double (*fp)(); definisce fp come variabile puntatore a una funzione che restituisce un double, nulla è indicato per i suoi parametri, quindi non vengono controllati (un compilatore C++ invece considera l’argomento come void) double (*fp)(double, double); definisce fp come variabile puntatore a una funzione che ha come parametri due double e restituisce un double 5 Definizione di variabili 6 Inizializzazione int (*fpv[3])(long); definisce fpv come vettore di 3 elementi, ciascuno è un puntatore a una funzione che ha come parametri un long e restituisce un int Le definizioni esterne inizializzano a NULL le variabili puntatori a funzioni Il nome di una funzione equivale al suo indirizzo di memoria (come per i vettori) Con le seguenti funzioni (prototipi): double pow(double,double); double atan2(double,double); int f1(long), f2(long); si possono avere le seguenti inizializzazioni: double (*fp)(double,double) = NULL; double (*fp)(double,double) = pow; int (*fpv[3])(long)={f1,f2}; /*+1NULL*/ int (*fpv[3])(long)={NULL};/*3 NULL*/ 7 Utilizzo con typedef typedef può definire un tipo puntatore a funzione con determinati parametri e valore restituito typedef double (*fpType)(double,double); dichiara fpType come tipo puntatore a funzione con due argomenti double e che restituisce un double fpType fp = pow; fpType fpv[5] = {pow,NULL,atan2}; Nota: fpv[3] e fpv[4] sono inizializzati a NULL perché l’inizializzazione dei primi elementi impone 0 (NULL) per i rimanenti 8 Assegnazione Il nome di una funzione equivale al suo indirizzo di memoria (come per i vettori) L’operatore & è ininfluente se applicato al nome di una funzione Esempi (l’operatore & può essere omesso): fp = &atan2; fp = atan2; fpv[2] = &f1; fpv[2] = f1; 9 Confronto 10 Chiamata della funzione È un normale confronto tra puntatori, permette ad esempio di determinare se il puntatore si riferisce ad una certa funzione o no oppure se è NULL if (fp == pow) printf("fp punta a pow\n"); La funzione il cui puntatore è stato memorizzato nella variabile fp può essere richiamata in due modi equivalenti: (*fp)(argomenti) fp(argomenti) Il primo metodo rende più evidente che si tratta di un puntatore a funzione e non del nome di una funzione, il secondo è più leggibile e comodo x = (*fp)(2.23, 4.76); x = fp(2.23, 4.76); i = (*fpv[1])(22); i = fpv[1](22); 11 Passaggio a funzione Un puntatore a funzione può essere passato ad una funzione come argomento Questo permette di passare ad una funzione [puntatori a] funzioni diverse per ottenere dalla stessa funzione comportamenti diversi Il parametro attuale deve essere il nome di una funzione Il parametro formale deve essere il prototipo delle funzioni chiamabili (è lì che viene definito il nome del puntatore visto dall’interno della funzione stessa) 12 Passaggio a funzione Esempio Si supponga di avere le due funzioni seguenti: int piu(int a,int b) fa la somma di 2 numeri interi int meno(int a,int b) fa la differenza di 2 numeri interi m = calc(12, 23, piu); calcola somma dei due valori n = calc(12, 23, meno); calcola la differenza dei due valori 13 14 Passaggio a funzione Valore restituito da funzione int piu(int a,int b) {return a+b;} int meno(int a,int b) {return a-b;} int calc(int x,int y,int (*funz)(int,int)) { return (*funz)(x,y); il nome funz viene dichiarato qui… } main() …e usato qui { int m, n; m = calc(12, 23, piu); n = calc(12, 23, meno); ... Una funzione può restituire un puntatore a funzione (2 metodi) Primo metodo Si definisce con l’istruzione typedef il tipo del puntatore-a-funzione restituito dalla funzione chiamata: typedef int (*TipoFunz)(int,int); TipoFunz è un nuovo tipo: puntatore-afunzione-che-ha-2-argomenti-int-erestituisce-un-int E lo si usa nel solito modo: TipoFunz operaz(int x); 15 Valore restituito da funzione typedef int (*TipoFunz)(int,int); int piu(int a,int b) {return a+b;} int meno(int a,int b) {return a-b;} TipoFunz operaz(int f) { static TipoFunz fpv[2]={piu,meno}; return fpv[f]; } static per poter usare main() il puntatore dopo che la { funzione è terminata int y; TipoFunz op; /* var. puntatore */ op = operaz(1); /* scelta op. */ y = (*op)(12, 23); } 16 Valore restituito da funzione Secondo metodo (meno chiaro) Si scrive ogni cast esplicitamente int (* operaz(int f))(int,int) { static int (* fpv[2])(int,int) = {piu,meno}; return fpv[f]; } La funzione definita è operaz e f ne è l’argomento la parte non sottolineata è il tipo della funzione restituita da operaz (dove (int,int) sono i parametri della funzione restituita) static è riferito a fpv 17 Valore restituito da funzione 18 Cast Secondo metodo (Continuazione) main() { int y; int (*op)(int,int);/*puntatore */ op = operaz(1); /* scelta op. */ y = (*op)(12, 23); } 19 Cast Il cast di puntatore-a-funzione può essere utilizzato per rendere una funzione compatibile con quella richiesta come argomento da un’altra funzione E’ indispensabile che gli argomenti siano comunque compatibili (in generale sono puntatori) Si veda l’esempio del qsort seguente Un cast di tipo puntatore-a-funzione “fa credere” al compilatore che la funzione a cui viene applicato abbia il tipo di quella del puntatore Si ricorda che il tipo di una funzione è definito dal tipo e numero dei parametri e dal tipo del valore restituito Il cast può essere premesso al nome di una funzione o a un puntatore a funzione Attenzione che il cast di tipo puntatore-afunzione non applica anche il cast agli argomenti e al valore restituito Passaggio a funzione con cast 20 qsort La funzione qsort in <stdlib.h> ordina un vettore, deve essere chiamata indicando il nome della funzione da usare per il confronto degli elementi, il suo prototipo è: void qsort( void *base, puntatore al vett da riordinare size_t n, sua dim (numero di elementi) size_t size, dim di ogni elemento (in byte) int (*cmp)(const void*,const void*)) cmp è il puntatore alla funzione da utilizzare per confrontare due elementi di quel vettore Passaggio a funzione con cast 21 qsort avere come argomenti due puntatori agli elementi del vettore da confrontare (qsort passerà a cmp gli indirizzi dei due elementi mediante &base[i]) restituire un valore int: <0 se il 1o elemento è minore del 2o se sono uguali 0 >0 se il 1o è maggiore del 2o La clausola const indica che gli oggetti da confrontare non saranno modificati Il tipo void* permette di passare puntatori di qualsiasi tipo: sarà la funzione passata ad utilizzarli ritrasformandoli al tipo corretto Passaggio a funzione con cast 23 qsort su vettore di interi 22 qsort su vettore di interi La funzione passata per cmp deve: Passaggio a funzione con cast La funzione passata cfr ha prototipo non identico a quello richiesto, ma compatibile (i puntatori sono sempre assegnabili gli uni agli altri anche se di tipo diverso); quindi viene fatto un cast della funzione passata: (int (*)(const void*,const void*))cfr I valori che la funzione qsort passa automaticamente a cfr per essere confrontati sono i vari &vett[i] che vengono usati come puntatori a int (quali sono) #include <stdlib.h> #include <stdio.h> int cfr(const int *a, const int *b) { if (*a < *b) return -1; else if (*a > *b) return +1; else return 0; } main() { int vett[10] = {3,2,5,4,7,9,0,1,6,8}; qsort(vett, 10, sizeof(int), (int (*)(const void*,const void*))cfr ); } Passaggio a funzione con cast 24 qsort su vettore di stringhe Per ordinare con qsort un vettore di stringhe char *vs[10] non si può utilizzare direttamente strcmp Gli elementi che qsort() passerebbe alla funzione di confronto sarebbero &vs[i], cioè i puntatori (indirizzi) ai puntatori agli elementi, mentre strcmp() richiede puntatori a char Passaggio a funzione con cast 25 qsort su vettore di stringhe qsort su vettore di strutture Bisogna definire una funzione ausiliaria: int cp(const void *p1,const void *p2) { return strcmp(*(char * const *)p1, *(char * const *)p2); } Gli argomenti di cp sono generici puntatori costanti a void (const void * ), il cast (char * const *) li riconverte nel loro vero tipo (puntatore a puntatore costante a char) e la deferenziazione produce un puntatore costante a char come la strcmp richiede 27 Esercizi 1. 2. Scrivere un programma che riordini con qsort un vettore di stringhe Scrivere un programma che 1. 2. 3. 4. Passaggio a funzione con cast definisca un vettore di 10 struct contenenti due valori interi x e y inizializzi il vettore ordini il vettore con qsort in base alla somma dei valori (se due somme sono uguali, il confronto è tra i primi membri, es. {4,6}>{3,7}) cerchi un elemento, dati i due valori in input, con la bsearch di <stdlib.h> e ne fornisca l’indice Per ordinare un vettore di struct bisogna definire una funzione di confronto ausiliaria: int structcmp(const void *p1, const void *p2) { const struct nome *sp1; const struct nome *sp2; sp1=(const struct nome *)p1; sp2=(const struct nome *)p2; if (sp1->membro > sp2->membro) return +1; else if (... 26