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