Puntatori a Funzioni e Callback. Massimo Benerecetti

Documenti analoghi
Linguaggio C: puntatori

Esercizio 2 (punti 7) Dato il seguente programma C: #include <stdio.h> int swap(int * nome, int length);

Implementazione di Liste puntate

Puntatori. Un puntatore contiene un numero che indica la locazione di memoria dove è presente la variabile puntata

Array e puntatori in C

Il linguaggio C. Notate che...

Corso di Informatica 1 Esercitazione n. 4

Consideriamo un vettore allocato dinamicamente

Lezione 9: Strutture e allocazione dinamica della memoria

Stringhe e allocazione dinamica della memoria

Indice PARTE A. Prefazione Gli Autori Ringraziamenti dell Editore La storia del C. Capitolo 1 Computer 1. Capitolo 2 Sistemi operativi 21 XVII XXIX

PILE E CODE. Pile (stack):

Esempio di Prova Scritta

Corso di Informatica 1 (IN1) Tutorato n. 11

Esercizio 1 Liste: calcolo perimetro di un poligono

Compito di Fondamenti di Informatica

ARRAY E STRINGHE. ESERCIZIO 2 Scrivere un programma che calcola il numero di doppie e di dittonghi (2 vocali vicine) presenti in una stringa.

I puntatori. Un puntatore è una variabile che contiene l indirizzo di un altra variabile. puntatore

Lezione 21 e 22. Valentina Ciriani ( ) Laboratorio di programmazione. Laboratorio di programmazione. Lezione 21 e 22

Array Tipi di dato semplici e strutturati strutturati array elementi omogenei numero d ordine indice lunghezza dimensione

Unità Didattica 5 Linguaggio C. Stringhe. Accesso a file ASCII. Strutture.

Strutture Dinamiche. Fondamenti di Informatica

Informatica 1. Corso di Laurea Triennale in Matematica. Gianluca Rossi

Primi passi col linguaggio C

Esercizi C su array e matrici

Inside C : Puntatori. Indirizzo: operatore & p = &v; x = a; Puntatori Referenziazione e Dereferenziazione Arrays

Alberi binari e alberi binari di ricerca

Esercitazione 6. Array

Elementi lessicali. Lezione 4. La parole chiave. Elementi lessicali. Elementi lessicali e espressioni logiche. Linguaggi di Programmazione I

Allocazione dinamica della memoria

Fondamenti di Informatica T. Linguaggio C: i puntatori

Caratteri e stringhe

Array multidimensionali e stringhe

Esercizio 1: Puntatori impiegati che sono manager

Programmazione. Cognome... Nome... Matricola... Prova scritta del 11 luglio 2014

Laboratorio di Algoritmi e Strutture Dati

Gli array. Gli array. Gli array. Classi di memorizzazione per array. Inizializzazione esplicita degli array. Array e puntatori

Struttura dati astratta Coda

Programmazione. Cognome... Nome... Matricola... Prova scritta del 22 settembre Negli esercizi proposti si utilizzano le seguenti classi:

Corso di Fondamenti di Informatica Il sistema dei tipi in C++

PROGRAMMI LINGUAGGIO C

Fondamenti di Informatica 2

4 Le liste collegate 4.0. Le liste collegate. 4 Le liste collegate Rappresentazione di liste 4.1 Rappresentazione di liste

Sommario PREFAZIONE...XI CAPITOLO 1: INTRODUZIONE AI COMPUTER, A INTERNET E AL WEB... 1 CAPITOLO 2: INTRODUZIONE ALLA PROGRAMMAZIONE IN C...

Struttura dei programmi C

I puntatori e l allocazione dinamica di memoria

Appello di Informatica B

Alberi e alberi binari I Un albero è un caso particolare di grafo

L Allocazione Dinamica della Memoria

Alberi. Gli alberi sono una generalizzazione delle liste che consente di modellare delle strutture gerarchiche come questa: Largo. Fosco.

puntatori Lab. Calc. AA 2007/08 1

Linguaggi di programmazione + laboratorio a.a. 2012/2013

Linguaggio C - sezione dichiarativa: costanti e variabili

Strutture dati e loro organizzazione. Gabriella Trucco

Quotazione compareto( ) Quotazione piurecente( ) Quotazione Quotazione Quotazione non trovato count( )

Stringhe. In C le stringhe ben formate sono in realtà array di caratteri terminati sempre da un carattere speciale, \0, detto anche

Definizione Allocazione e deallocazione di variabili Allocazione e deallocazione di vettori

2. Spiegare brevemente qual è la funzione del compilatore e la sua importanza per il programmatore.

Rappresentazione di liste mediante puntatori in linguaggio C

Scrittura formattata - printf

FONDAMENTI DI INFORMATICA

ESERCIZI DI PROGRAMMAZIONE C/C++ per le classi seconde

Linguaggio C: le funzioni

Transcript:

Puntatori a Funzioni e Callback Massimo Benerecetti

Puntatori a Funzione Un «puntatore a funzione» è un puntatore che punta all indirizzo di memoria in cui è contenuto il codice eseguibile di una funzione. La memoria indirizzata da un puntatore a funzione si trova nel textsegment di memoria associata al processo (memoria statica). La memoria indirizzata da un puntatore a funzione non deve, quindi, essere allocata. Importante: un puntatore a funzione punta sempre a una funzione con una specifica segnatura. Lo stesso puntatore può indirizzare funzioni diverse ma con gli stessi (tipi di) parametri e stesso tipo di valore di ritorno.

Funzioni e Puntatori a Funzione Prototipo di funzione che restituisce un intero e non riceve parametri: int GetInputValue(void); Dichiarazione di un puntatore a funzione (i.e., GetValue) che restituisce un intero e non riceve parametri: int (*GetValue)(void); Dichiarazione del tipo FNINPUT per puntatori a funzione che restituiscono un intero e non ricevono parametri: typedef int (*FNINPUT)(void); Dichiarazione di una variabile di tipo FNINPUT (puntatore a funzione): FNINPUT GetValue;

Esempio typedef int (*FNINPUT)(void); int main(void) { int ret; FNINPUT GetValue;... GetValue = &GetInputValue; /* Assegnazione a variabile di tipo */ GetValue = GetInputValue; /* puntatore a funzione, assegnato */ /* all'indirizzo della funzione */... /* "GetInputValue()" */ ret = GetValue(); /* Chiamata di funzione tramite puntatore */ ret = (*GetValue)(); /* Chiamata di funzione tramite puntatore */...

Funzioni di Callback Una funzione di callback è una funzione chiamata tramite un puntatore a funzione. Supponiamo che a una funzione venga passato come argomento il puntatore (indirizzo) di un altra funzione. Quando la pima funzione usa il puntatore a funzione per chiamare la seconda funzione, viene effettuata una callback. Le callback risultano particolarmente utili per rendere il codice più generale, disaccoppiando la funzione chiamante dalla funzione chiamata. Può essere utile nella definizione di librerie (es. libreria di liste generiche, di alberi, ordinamenti di generici elementi, etc.) e, in generale, per l implementazione di Tipi di Dati Astratti. Rendere il codice delle funzioni indipendente da dati specifici la cui tipologia non ha impatto sulla logica del loro funzionamento.

Esempio di Callback 1 Si supponga di voler definire una funzione che, dato un array A, applichi ad ogni suo elemento l operazione di elevamento a quadrato. Array_Square(int *A, int dim) for (i=0; i < dim; i++) A[i] = A[i] * A[i]; Supponiamo ora di voler definire una funzione che, dato un array A, applichi ad ogni suo elemento l operazione di incremento. Array_Incr(int *A, int dim) for (i=0; i < dim; i++) A[i] = A[i] + 1;

Esempio di Callback 1 Possiamo definire un unica funzione Array_Op che applica una qualche operazione a tutti gli elementi dell array A. typedef int (*FN_op)(int); int square(int a) { return (a * a); int increment(int a) { return (a + 1); int double(int a) { return (2 * a); Array_Op(int *A, int dim, FN_op op) { for (i=0; i < dim; i++) A[i] = op(a[i]); int main(void) { Array_op(A,n,&square); Array_op(A,n,&increment); Array_op(A,n,&double);

Esempio di Callback 2 Si supponga di voler definire una libreria di gestione liste di interi (o di qualche altro tipo). Il popolamento della lista può avvenire in vari modi: ad es. tramite input diretto dell utente o tramite generazione casuale di interi. Possiamo rendere la gestione dell inserimento degli interi indipendente dalla modalità di generazione dei valori, utilizzando una callback GetValue().

Popolamento di una lista lista *PopolaLista(lista *L, int nelem) { /* Popola una lista ordinata L inserendo nelem elementi utilizzando una generica funzione GetValue() per ricevere i valori da inserire. */ int elem, i; for (i=0; i < nelem; i++) { elem = GetValue(); L = InserisciOrd(L,elem); PrintLista(L); return(l);

Esempio Callback 2 int GetInputValue(void) { int k; printf("inserisci un valore intero: "); scanf("%d", &k); return(k); int GetRandomValue(void) { return( rand()%100 ); /* valore pseudo-casuale tra 0 e 99 */

typedef struct List { int key; struct List *next; lista; Esempio Callback 2 int main(void) { lista *L1=NULL; lista *L2=NULL; L1 = PopolaLista(L1, 20, &GetInputValue); PrintLista(L1); L2 = PopolaLista(L2, 20, &GetRandomValue); PrintLista(L2);

Esempio Callback 2 /* Dichiarazione di tipo puntatore a funzione */ typedef int (*FNINPUT)(void); lista *PopolaLista(lista *L, int nelem, FNINPUT GetValue) { /* Funzione che popola una lista L inserendo nelem elementi utilizzando la callback GetValue() per ricevere i valori da inserire. */ int elem, i; for (i=0; i < nelem; i++) { elem = GetValue(); /* oppure (*getvalue)() */ L = InserisciOrd(L,elem); PrintLista(L); return(l);

Dati generici e puntatori void Spesso può essere utile dichiarare variabili che possano contenere tipi di dati diversi. Il C non ammette, però, variabili di tipo generico void: void a; /* dichiarazione non valida */ Ammette, invece, puntatori a dati di tipo generico void: void *p; /* dichiarazione valida */ Un puntatore void può essere usato per indirizzare qualsiasi tipo di dato int x; void p = &x; / p punta a un int / float f; void p = &f; / p punta a un float / L aritmetica dei puntatori tratta tutti i puntatori a void come puntatori a singoli byte, quindi: se int *x; e void p = x; allora (p + i) (x + i)

Dereferenziazione di puntatori a void I puntatori a void non possono essere dereferenziati direttamente void p; printf ("%d", p); / non valido / Devono essere sempre sottoposti a cast prima di accedere al loro contenuto: void p; printf ("%d", ((int *)p); / valido / Analogamente, l assegnamento di un valore al contenuto di un puntatore a void necessita di cast: int x; void p = &x; *p = 5; / non valido / int x; void p = &x; *((int *)p) = 5; / valido / In alternativa, è possibile usare la funzione di libreria memcpy(dest,source,n_byte) per copiare un numero dato di byte contigui da un qualsiasi indirizzo sorgente a un indirizzo destinazione, indipendentemente dal tipo didati contenuti: int x; int y = 5; void p = &x; memcpy(p,&y,sizeof(int)); / valido /

Esempio di Callback 3 Si supponga di voler definire una funzione generale per il popolamento di un qualsiasi array, che possa funzionare per ogni tipo di dato. Tipi di dati diversi dovranno essere generati in modo differente (ad. interi, float, record/struct ecc.). Per i valori numerici (int/float) vogliamo permettere anche una generazione di valori casuali. Possiamo definire una funzione PopolaArray che utilizza una callback GetValue() per generare gli elementi specifici dell array che deve popolare, indipendentemente dal tipo di dato dell array.

Esempio di Callback 3 typedef void (*GETIN)(void *); typedef struct STRNOME { char nome[30]; char cognome[30]; NOME; int main(void) { NOME array1[10]; float array2[10]; int array3[10]; PopolaArray(array1,10, sizeof(nome), &GetInputNome); PopolaArray(array2,10, sizeof(float), &GetRandomFloat); PopolaArray(array3,10, sizeof(int), &GetRandomInt);

Esempio di Callback 3 void PopolaArray(void *A, int nel, int dimel, GETIN GetValue) { int elem, i; void *elemento = malloc(dimel); for (i=0; i<nel; i++) { GetValue(elemento); CopiaDato(A,i,elemento,0,dimel); free(elemento); void CopiaDato(void *dest,int dpos, void *src,int spos, int dim) { void *dst_addr = dest+(dpos*dim); void *src_addr = src+(spos*dim); memcpy(dst_addr,src_addr,dim);

Esempio di Callback 3 void GetInputInt(void *k) { printf("inserisci un valore intero: "); scanf("%d", (int *)k); void GetRandomInt(void *k) { /* intero casuale 0 k < 100 */ *((int *) k) = rand()%100;

Esempio di Callback 3 void GetInputFloat(void *k) { printf("inserisci un valore float: "); scanf("%f", (float *)k); void GetRandomFloat(void *k) { /* float casuale 0 k < 100 */ float d = (float)rand()/(float)rand_max; *((float *) k) = (rand()%100) + d;

Esempio di Callback 3 void GetInputNome(void *elem) { NOME *temp = (NOME *)elem; printf("inserisci un nome (max 30): "); scanf("%s", temp->nome); printf("inserisci un cognome (max 30): "); scanf("%s", temp->cognome);

Esempio di Callback 4 typedef int (*COMPAREFN)(void *,void *); int ConfrontaInt(void *a, void *b) { int inta = *((int *)a); int intb = *((int *)b); return ((inta > intb)? 1 :((inta < intb)? -1 : 0)); int main(void) { int array[100]; PopolaArray(array,100,sizeof(int),&GetRandomInt); InsertionSort(array,100,sizeof(int),&ConfrontaInt);

Ordinamento per array generico Si supponga di voler generalizzare l algoritmo di InsertSort per ordinare array che possono contenere vari tipi di dati. Tipi di dati diversi hanno, infatti, diverse logiche di confronto (ad. interi, stringhe, ecc.). Possiamo definire InsertSort in modo che utilizzi una callback Compare() per confrontare gli elementi del generico array che deve ordinare. Compare() punterà a diverse funzioni di confronto, a seconda del tipo di dati correntemente contenute nell array

Algoritmo di InsertionSort InsertSort(A) for j = 1 to Lenght(A)-1 do Key = A[j] i = j-1 while (i 0 and A[i] > Key) do A[i+1] = A[i] i=i-1 A[i+1] = Key

Esempio di Callback 4 void InsertSort(Array_T A, int dim, int el_dim, COMPFN Compare) { void *key = malloc(el_dim); for (j = 1; j < dim; j++) { CopiaDato(key,0,A,j,el_dim); /* key = A[j] */ i = j-1; while(i>=0 && Compare(A+(i * el_dim), key)>0) { CopiaDato(A,i+1,A,i,el_dim); /* A[i+1] = A[i] */ i--; CopiaDato(A,i+1,key,0,el_dim); /* A[i+1] = key */ free(key);

Implementazione di ADT Tramite il meccanismo dei puntatori a tipo void e delle callback è, quindi, possibile definire in C Tipi di Dati Astratti (ADT). Come esempio, potremmo definire un ADT array nel seguente modo: struct Array { void *elem[max_elem]; int nelem; ; e associare ad esso i relativi operatori.

Esempio ADT typedef struct Array *Array_T; typedef void (*FNFREE)(void *); Array_T Array_new(void) { Array_T array = malloc(sizeof(struct Array)); array->nelem = 0; return array; void Array_free(Array_T array, FNFREE freedata) { int i; if (freedata) for (i=0; i<array->nemel; i++) freedata(array->elem[i]); free(array); int Array_length(Array_T array) { return array->nelem;

Esempio ADT void *Array_data(Array_T array, int idx) { return array->elem [idx]; void Array_append(Array_T array, void *datap) { int idx = array->nelem; if (idx < MAX_ELEM - 1) { array->elem [idx] = datap; array->nelem++; void Array_replace(Array_T array, int idx, void *datap) { array->elem [idx] = datap;

Esempio ADT void Array_insert(Array_T array, int idx, void *datap) { int i; if (array->nelem < MAX_ELEM-1 && idx >= 0 && idx < MAX_ELEM-1) { for (i = array->nelem; i > idx; i--) array->elem[i] = array->elem [i-1]; array->elem[idx] = datap; array->nelem++; void Array_remove(Array_T array, int idx) { int i; if (array->nelem > 0 idx >= 0 && idx < MAX_ELEM) { for (i = idx; i < array->nelem; i++) array->elems[i-1] = array->elem [i]; array->nelem--;

Esempio ADT void Array_InsertSort(Array_T array, COMPFN Compare) { void *key; int j,i; for (j = 1; j < array->nelem; j++) { key = array->elem [j]; i = j-1; while(i>=0 && Compare(array->elem [i], key) > 0) { array->elem[i+1] = array->elem[i]; --i; array->elem[i+1] = key);

#include array.h #include <stdio.h> #include <string.h> int main() { int i; Array_T array = Array_new(); Esempio ADT Array_append(array, (void *) IS ); Array_append(array, (void *) FUN ); Array_append(array, (void *) COS217 ); Array_InsertSort(array, &strcmp); for (i = 0; i < Array_length(array); i++) { char *str= (char *)Array_data(array, i); printf( %s,str); Array_free(array, NULL);

Esercizio Definire una libreria per lo ADT Albero Binario di Ricerca. Un elemento dell ADT è un ABR i cui nodi possono contenere un dato di tipo qualsiasi, sia un dato di tipo predefinito che di tipo definito dall utente. 1. Estendere la libreria di ARB nell esercizio 1 del corso, in modo che possa gestire ARB generici. 2. Definire una struttura per il nodo della lista in grado di contenere un qualsiasi tipo di dato. 3. Definire diverse funzioni di costruzione e distruzione del dato, in base al tipo di dato da costruire (interi, reali, stringhe, record composti, ) da utilizzare come callback. 4. Definire le funzioni di confronto tra due elementi, dipendenti dal tipo dei dati da confrontare e da utilizzare come callback.