puntatori a funzioni

Upload: alexlhhcas

Post on 07-Oct-2015

10 views

Category:

Documents


0 download

DESCRIPTION

C puntatori a funzioni

TRANSCRIPT

  • Puntatori a funzione

    untatore ormai la parola magica del linguaggio C++. Ci sono programmatori che non riescono ad immaginare la loro esistenza senza questa importante struttura prevista fin dai tempi del C.

    Ma mentre tutti i neofiti del linguaggio familiarizzano presto con i puntatori ai vari tipi di dati (basti pensare al passaggio di un vettore ad una funzione), passa molto tempo prima che essi raggiungano la conoscenza dei puntatori a funzioni: anzi, non sarebbe troppo azzardato dire che molti giovani programmatori ignorano del tutto questa potente funzionalit del linguaggio.

    P In queste pagine cercheremo quindi di mostrare questa tecnica molto utile e potente, che permette addirittura di passare una funzione come argomento di unaltra! In questa guida cercheremo di ripercorrere alcuni aspetti pi interessanti dei puntatori ai dati, ossia la loro rappresentazione in memoria, per poi arrivare a parlare dei puntatori a funzione, e vederne tutte le potenzialit. La discussione del primo paragrafo analizza alcuni casi di puntatore a dato e mostra come sono puntati i vari tipi di dati e come la tipizzazione dei puntatori in C++ faciliti alcuni compiti. Vengono mostrati alcuni esempi scritti in Assembler per il processore Intel 8086, che utilizzano solamente le istruzioni pi semplici e banali. La conoscenza di un qualunque tipo di linguaggio Assembler pu facilitare la lettura del paragrafo. La seconda parte invece legata esclusivamente ai puntatori a pezzi di codice, in particolare alle funzioni. Per leggere e comprendere questa guida necessario conoscere bene la sintassi e lutilizzo basilare dei puntatori in C++, almeno come funzionano, come si utilizzano e come vengono sostituiti ai vettori.

    1

  • 1. Analisi approfondita dei puntatori a dato

    1.1 Puntatore a carattere

    Supponiamo di avere un dato di tipo carattere. Esso rappresentato, su tutti i sistemi, con un solo byte, quindi il suo puntatore punter sempre ed esattamente al dato. [C++] Puntatore a char // Dichiarazione delle variabili char x; char* p; // Assegnazione di un valore x = C; // Puntiamo al carattere p = &x; // Stampiamo il carattere cout

  • 1.2 Puntatore ad intero

    Fintanto che un puntatore punta ad un tipo semplice come un carattere non sorge quindi alcun problema. Quando invece vogliamo puntare ad un tipo leggermente pi complesso le cose cambiano. Facciamo lesempio di un puntatore ad un intero a 2 byte. In C++ la gestione non cambia minimamente, cos come non cambia nella versione Assembler. Tuttavia nella rappresentazione in memoria si nota qualcosa di diverso. [C++] Puntatore a intero // Dichiarazione delle variabili int x; int* p; // Assegnazione di un valore x = 4676; // Puntiamo al numero p = &x; // Stampiamo il numero cout

  • Otteniamo quindi che la parte bassa del numero (44h) finisce nella cella bassa di memoria, mentre la parte alta (12h) finisce nella cella successiva. Il puntatore, che esattamente un numero in esadecimale, pu puntare ad una sola locazione in memoria. Punta, come visto, alla prima locazione del numero, ossia quella della parte bassa. Non abbiamo avuto difficolt nel trasferimento in Assembler in quanto anche il processore 8086 pu trasferire 16 bit contemporaneamente. Se analizziamo un caso ancora pi complesso, ad esempio il trasferimento di un numero float, ci accorgiamo che il puntatore sempre alla prima delle quattro locazioni utilizzate per rappresentare il numero, ed in Assembler sarebbe necessario spostare manualmente le due parti in cui viene spezzato il numero (da 2 byte ciascuna). 1.3 Puntatore a vettore

    Passiamo ora ad analizzare un caso pi complesso: lutilizzo dei vettori. Ancora una volta, partiamo dal caso pi semplice: ogni elemento del vettore occupa un byte, parliamo quindi di un vettore di caratteri. Per utilizzare un caso pi classico, osserviamo un caso particolare di vettore di caratteri: la stringa. Vediamo come avviene la sua gestione in C++. [C++] Puntatore a stringa // Dichiarazione della stringa char str[] = Ciao mondo; // Puntatore di tipo carattere char* p; // Puntiamo alla stringa p = str; // Stampiamo la stringa cout

  • [C++] Puntatore a stringa (V.2) // Dichiarazione della stringa char str[] = Ciao mondo; // Puntatore di tipo carattere char* p; // Puntiamo alla stringa p = &str[0]; // Equivale a p = str // Stampiamo la stringa for ( ; *p ; p++) cout
  • [ASM] Puntatore a stringa .DATA ; Definizione della stringa STR db Ciao mondo$ .CODE ; Puntiamo alla stringa LEA SI , STR ; Preleviamo ogni carattere Ciclo: CMP [SI] , $ ; La stringa terminata? JZ FCicl MOV AL , [SI] ; Preleviamo il carattere INC SI ; Prossimo carattere FCicl:

    Anche in questa versione Assembler, non abbiamo fatto fare nulla al programma, se non spostare ogni carattere nel registro AL. Soffermiamoci ora sullistruzione INC SI Come si nota, il puntatore alla stringa viene incrementato di una sola unit (questo il compito svolto dallistruzione INC). Ci sarebbe stato equivalente a scrivere ADD SI , 1 Abbiamo quindi verificato come sia in C++ che in Assembler un puntatore a carattere venga ogni volta incrementato di una sola unit. Proviamo ora a ripetere lesempio per stampare un vettore di interi, supponendo sempre di lavorare in un sistema in cui gli interi sono a 2 byte. La versione C++ mostra chiaramente i passaggi che ci interessano: [C++] Puntatore a vettore di interi // Vettore di interi int v[5]; // Puntatore al vettore int *p; // Inizializzazione puntatore p = &v[0]; // Equivalente a p = v // Riempimento vettore for (int i = 0; i < 5; i++) *(p+i) = i;

    Il codice riempie il vettore in modo che ad ogni elemento corrisponda valore uguale al proprio indice.

    6

  • Da notare listruzione *(p+i) = i; che del tutto equivalente a p[i] = i; e, considerando a cosa punta p, anche a v[i] = i; In realt notiamo che ad ogni iterazione ci spostiamo di un elemento nel vettore, quindi la cosa puntata dal puntatore diventa quella successiva. Ma come?! Se gli interi sono a 2 byte e noi ci spostiamo di ununit, come possibile che il programma funzioni? Funziona eccome, invece. E lo fa grazie al fatto che i puntatori in C sono tipizzati. Quando diciamo al programma che stiamo utilizzando un puntatore ad intero, esso sa sempre che un intero a 2 byte. Ogni incremento di ununit, viene interpretato come ununit del tipo. Lavorando con gli interi, ogni incremento di ununit equivale effettivamente a 2 celle di memoria! Potremmo quindi esplicare il tutto. Dato un puntatore p tipo* p; sempre vera la seguente uguaglianza p + i = p + i * sizeof (tipo) Se lavorassimo con dati float, ogni incremento sarebbe di 4 celle di memoria e cos via. In memoria si avrebbe la situazione mostrata in figura.

    In memoria abbiamo indirizzi sempre crescenti, ma il nostro puntatore cresce di due celle ogni volta. Cos, P + 1, che corrisponde al secondo elemento del vettore, in realt si trova a 2 celle di distanza in memoria, non subito adiacente. In C++ questo incremento rispetto al tipo puntato

    V 00 P

    V + 1 00 V + 2 01 P + 1 00

    02 P + 2 00 03 P + 3 00 04 P + 4 00

    7

  • automatico: la stessa cosa non vale in Assembler, dove non esistono i puntatori ai tipi di dato Questo significa che il programmatore deve considerare la grandezza del tipo di dato con cui sta lavorando ed effettuare nel modo opportuno ogni tipo di incremento e modifica del puntatore, per evitare di puntare ad aree di memoria che non significano niente per noi. Se infatti il puntatore venisse incrementato di una sola unit, prelevando dopo 2 byte per un intero, si otterrebbe un valore formato per met da un numero e per laltra met da quello successivo. Per eseguire lo stesso programma avremmo quindi dovuto scrivere qualcosa di lievemente pi complesso. [ASM] Puntatore a vettore di interi .DATA ; Definiamo il vettore V dw 5 DUP (?) L EQU $ - V ; Lunghezza del vettore .CODE ; Puntatore LEA SI , V ; Contatore MOV CX , L ; Posizione nel vettore MOV DX , 0 ; Riempiamo il vettore Ciclo: MOV [SI] , DX ADD SI , 2 INC DX LOOP Ciclo

    Come detto, il puntatore deve essere incrementato manualmente di due unit, mentre la posizione allinterno del vettore di una sola. Ancora una volta abbiamo sfruttato la possibilit di trasferire 16 bit contemporaneamente. Se ci non fosse stato possibile, il programma sarebbe diventato molto pi complesso: [ASM] Puntatore a vettore di interi .DATA ; Definiamo il vettore V dw 5 DUP (?) L EQU $ - V ; Lunghezza del vettore .CODE

    8

  • ; Puntatore LEA SI , V ; Contatore MOV CX , L ; Posizione nel vettore MOV DH , 0 MOV DL , 0 ; Riempiamo il vettore Ciclo: MOV [SI] , DL ; Parte bassa MOV [SI + 1] , DH ; Parte alta ADD SI , 2 ADD DL , 1 ; Prossima posizione ADC DH , 0 ; Spazio per riporti LOOP Ciclo

    Nonostante abbiamo ancora semplificato il nostro compito (ad esempio, abbiamo usato listruzione LOOP, che lavora a 16 bit), si pu comunque evidenziare come le operazioni si siano abbastanza allungate. Per trasferire pi di due byte sono comunque necessarie pi operazioni. In generale, a seconda del sistema, il trasferimento di un dato pu richiedere pi di un passaggio, se il sistema non in grado di lavorare con tutti i bit necessari contemporaneamente.

    9

  • 2. Puntatori a funzioni

    2.1 Introduzione

    Cos come i dati sono contenuti in memoria (nellarea dati), anche il codice lo , e si trova nellarea codice. E ovvio che sia cos, altrimenti il nostro microprocessore non sarebbe in grado di individuarlo ed eseguirlo. Pertanto, ogni istruzione ha un indirizzo fisico in memoria: per questo motivo non vi alcuna ragione per cui unistruzione non debba essere puntata da un puntatore. In C++ questa tecnica permessa in parte, nel senso che possibile puntare ad una funzione. Se si ha qualche basilare conoscenza del linguaggio macchina, probabilmente si sapr come sono formate le istruzioni allinterno del processore: da un codice operativo, che definisce loperazione da eseguire e il registro su cui operare, pi un eventuale numero che pu definire un operando immediato od una locazione di memoria in cui presente un dato (secondo operando). Un blocco di codice quindi cos presentato in memoria (i valori nelle locazioni di memoria sono inseriti a caso).

    Il blocco di codice contiene una serie di istruzioni (ad indirizzi crescenti e consecutivi). Se il blocco inizia con listruzione 01 e termina con la 06, e questo blocco una funzione, allora un puntatore ad esso sar indirizzato sempre verso il punto dentrata (istruzione 01).

    Op.Code 01 05 P Numero 01 A0

    Op.Code 02 4C Op.Code 03 AA Numero 03 03

    Op.Code 04 05 Numero 04 5F

    Op.Code 05 67 Op.Code 06 4D Numero 06 30

    2.2 Sintassi

    Supponiamo di definire due funzioni in C++, chiamate somma() e prodotto(). [C++] Funzione somma() int somma (int a, int b) { return a + b; }

    10

  • [C++] Funzione prodotto() int prodotto (int a, int b) { return a * b; }

    Analizziamo le somiglianze tra queste due funzioni:

    1. Esse restituiscono un valore dello stesso tipo (int) 2. Esse accettano un numero uguale di argomenti (2) 3. Esse accettano lo stesso tipo di argomenti (int, int)

    In effetti, come si ricorda, una funzione identificata da:

    Tipo di dato restituito Nome della funzione Numero, nome e tipo degli argomenti

    Bene, se sono costanti il primo ed il terzo punto (il nome degli argomenti facoltativo), allora le due funzioni possono essere puntate da uno stesso puntatore a funzione. Un puntatore a funzione tiene costante il tipo restituito e gli argomenti, quindi pu accettare un qualunque nome. Pertanto possiamo avere un puntatore ad una funzione che restituisce un intero e accetta due caratteri, o che restituisce void e accetta due reali e cos via Pertanto un puntatore a funzione definito da:

    Nome del puntatore Tipo restituito dalle funzioni puntate Numero e tipo degli argomenti delle funzioni puntate

    Vediamo quindi la sintassi per la creazione di un puntatore a funzione. tipo-restituito ( * nome-puntatore ) ( lista-parametri ) Nel nostro caso, per creare un puntatore adeguato ad entrambe le funzioni somma() e prodotto(), avremmo dovuto scrivere: int (*p)(int,int);

    11

  • Abbiamo cos definito un puntatore ad una funzione che accetta due argomenti di tipo intero e restituisce un intero. Da notare subito che le parentesi (*p) sono necessarie, per una questione di priorit degli operatori. Se avessimo scritto solamente int *p (int,int); Il compilatore avrebbe interpretato listruzione come un prototipo ad una funzione che accetta due parametri interi e restituisce un int* (ossia un puntatore ad intero); del puntatore a funzione non ci sarebbe stata traccia. Volendo vi potreste accontentare del fatto che le parentesi sono necessarie per una questione di priorit, ma vale la pena aprire una parentesi per dare una motivazione pi esaustiva, in modo da chiarire meglio il concetto. Entriamo allora nel merito della questione e partiamo analizzando la tavola delle priorit degli operatori C++. La tavola tratta dal manuale di riferimento ufficiale del linguaggio, tratta dal sito ufficiale, www.cplusplus.com. Priorit Operatori Descrizione Associativit 1 :: Ambito Sinistra 2 () [ ] -> . sizeof Sinistra

    ++ -- Incremento e Decremento ~ Complemento a 1 (bit a bit)! NOT unario & * Puntatori e Reference (type) Casting di tipo

    3

    + - Segno

    Destra

    4 * / % Operazioni aritmetiche Sinistra 5 + - Operazioni aritmetiche Sinistra 6 > Shift (bit a bit) Sinistra 7 < >= Operatori relazionali Sinistra 8 == != Operatori relazionali Sinistra 9 & ^ | Operatori bit a bit Sinistra 10 && || Operatori logici Sinistra 11 ?: Operatore condizionale Destra 12 = += -= *= /= %= >>=

  • Riguardiamo ora la nostra espressione errata: int *p (int,int); Il compilatore, utilizzando le precedenze imposte, stabilisce per prima cosa che ci sono dei simboli vicino allidentificatore p. Deve scegliere come interpretare lasterisco (*) e la parte tra parentesi. Seguendo la tabella prevalgono queste ultime (priorit 2 contro 3). Il compilatore decreta quindi che p una funzione con due argomento interi. A questo punto associa lasterisco con int per arrivare al tipo restituito (puntatore ad intero). Vediamo ora cosa succede con la versione corretta: int (*p)(int,int); Il compilatore trova due coppie di parentesi: seguendo la tabella, si segue lassociativit a sinistra, quindi viene analizzato prima lidentificatore *p, quindi la coppia di argomenti. Si stabilisce quindi che *p accetta due argomenti interi. Siamo di fronte ad un puntatore a funzione che accetta due argomenti interi. Chiusa la parentesi, torniamo agli aspetti puramente sintattici. Abbiamo dichiarato un puntatore a funzione, ora dobbiamo assegnargli qualcosa. Ovviamente possiamo assegnare solo qualcosa di accettabile, ossia una funzione che restituisce un intero e accetta due interi come argomenti. Possiamo allora assegnargli una delle due funzioni create prima (somma() e prodotto()). Essendo la variabile di tipo puntatore, il suo contenuto dovr essere un indirizzo. Come succede con un vettore, possibile ottenere lindirizzo di una funzione (in particolare, del suo punto dentrata, ossia dellinizio) semplicemente scrivendo il suo nome senza nessuna parentesi di seguito. E inoltre disponibile la versione che antepone il carattere di indirizzo (&) davanti al nome della funzione, senza argomenti. Punto dingresso di una funzione nome-funzione &nume-funzione

    13

  • Ecco le due possibili assegnazioni. [C++] Assegnazione di una funzione // Dichiaro il puntatore int (*p)(int,int); // Gli assegno la funzione somma p = somma; // Gli assegno la funzione prodotto p = prodotto;

    A questo punto il puntatore indirizzato verso una funzione. Come possiamo fare per utilizzarne il contenuto? Semplice, come abbiamo sempre fatto con un qualunque puntatore, ossia usando loperatore * per dereferenziarlo. Una volta dereferenziato, tuttavia, necessario passare i parametri attuali alla funzione, in modo che questa venga utilizzata. Il programma sottostante (completo) mostra un semplice esempio di utilizzo di funzioni puntate. [C++] Utilizzo di funzioni puntate #include #include using namespace std; int somma (int a, int b) { return a + b; } int prodotto (int a, int b) { return a * b; } int main(int argc, char *argv[]) { int (*p)(int,int); // Utilizzo della funzione somma p = somma; cout

  • Le due forme sono del tutto equivalenti, ma la prima formalmente pi corretta dal punto di vista teorico ed ha inoltre lenorme vantaggio di essere pi chiara e leggibile. La dichiarazione di un puntatore a funzione pu essere lunga e laboriosa. Se due o pi volte deve essere riscritto lo stesso puntatore, assai facile cadere in errore: per questo fortemente consigliato lutilizzo di unistruzione typedef per ridefinire lintero tipo di puntatore. La sintassi da utilizzare la seguente: typedef tipo-rest (*alias) (lista-parametri) Ad esempio, se viene scritta listruzione typedef int (*punt1)(int,int) Ogni dichiarazione del tipo punt1 a; viene automaticamente convertita in int (*a)(int,int); In determinati casi questa sintassi si pu rivelare particolarmente utile e non c alcun motivo per cui non valga veramente la pena utilizzarla. Lunico consiglio di fornire sempre una documentazione appropriata e di dare nomi significativi ai vostri alias. Laritmetica dei puntatori non vale per i puntatori a funzione. Tuttavia sono disponibili gli operatori di confronti (!= e ==) per verificare se un puntatore punta ad una determinata funzione o se ha valore NULL. 2.3 Utilizzo dei puntatori a funzione

    Conclusa la parte sintattica, rimane ora da vedere come possibile utilizzarli in determinate occasioni, per dimostrarne leffettiva importanza. Una delle applicazioni pi frequenti quella di passare una funzione come argomento di unaltra funzione. E sufficiente infatti passare un puntatore e chiamare la funzione dal corpo della principale.

    15

  • Prima di scrivere noi una funzione che ne accetta unaltra come argomento, osserviamo il codice scritto da altri e cerchiamo di capirne lutilizzo. Abbiamo esempi di puntatori a funzioni anche nella libreria standard del C++. In particolare, dentro la cstdlib, sono presenti alcune funzioni di ordinamento e ricerca che possono operare su qualsiasi tipo di dato. Essendo le funzioni disponibili gi dai tempi del linguaggio C, esse non utilizzavano i template, ma lavoravano con puntatori void*, che, come probabilmente si sa, sono puntatori generici che vengono poi tipizzati allinterno del corpo della funzione. Tuttavia, siccome le funzioni di ordinamento e ricerca devono eseguire confronti, necessario stabilire una funzione per i confronti tra i vari tipi. Essendo queste funzioni spesso diverse per i vari tipi di dati (basti pensare alle classi definite dallutente), necessario per queste funzioni sapere come comparare gli elementi: per farlo utilizza una funzione che riceve come argomento. Questa tecnica si utilizza essenzialmente per il fatto che molti algoritmi sono praticamente identici sia che si lavori con un tipo di dato che con un altro: la ricerca binaria, ad esempio, scansione sempre allo stesso modo un vettore ed effettua dei confronti tra determinati elementi. Se viene stabilito lo standard con cui si effettua il confronto, il resto sempre uguale. Proviamo allora a studiare la funzione bsearch() della libreria cstdlib. void* bsearch (

    const void * key, const void * base, size_t num, size_t width, int (*fncompare)(const void *, const void * )

    ); Analizziamo ora ogni suo argomento. const void* key Puntatore alloggetto da ricercare allinterno del vettore. Deve ovviamente essere dello stesso tipo del vettore. const void* base Vettore su cui effettuare la ricerca.

    16

  • size_t num Numero di elementi del vettore. size_t width Dimensione di ogni elemento del vettore. E sufficiente settarlo a

    sizeof (tipo) int (*fncompare) (const void *, const void * ) Puntatore ad una funzione di confronto. Questa funzione deve accettare due puntatori al tipo del vettore e restituire un valore intero che rispecchi la seguente tabella:

    Il principio di funzionamento analogo a quello utilizzato dalla funzione strcmp() per confrontare due stringhe.

    Il tipo restituito dalla funzione un puntatore allelemento trovato, se presente, oppure un puntatore NULL nel caso la ricerca non abbia esito positivo. Si ricorda che il vettore di input deve essere ordinato in ordine crescente, altrimenti lalgoritmo di ricerca binaria non eseguibile. Vediamo quindi un programma che utilizza questa funzione. [C++] Ricerca binaria #include #include using namespace std; int compare (const void* a, const void* b) { return (*(int*)a - *(int*)b); } int main(int argc, char *argv[]) { // Vettore int v[100]; // Lettura vettore int n; cout > n; for (int i = 0; i < n; i++) { cout elem2 > 0

    17

  • // Elemento da cercare int cerca; cout > cerca; // Ricerca int* p = (int*) bsearch (&cerca, v, n, sizeof (int), compare); if (!p) cout
  • Vediamo il codice completo del programma. [C++] Ordinamento vettore di stringhe #include #include #include using namespace std; int compare (const void* a, const void* b) { return strcmp ((char*)a,(char*)b); } int main(int argc, char *argv[]) { // Vettore di stringhe char str[100][80]; int n; cout > n; for (int i = 0; i < n; i++) { cout > str[i]; } cout
  • In questo modo i valori restituiti sono simmetrici rispetto a quelli originali e lordinamento avviene in modo contrario. Proviamo a questo punto a scrivere noi una funzione che utilizzi i puntatori a funzioni, in modo da vederne il funzionamento nel suo complesso. Per prima cosa proviamo a scrivere una versione di ricerca binaria, esattamente come fa la bsearch(). Stabiliamo di tenere costanti gli stessi parametri della funzione di libreria. Unimplementazione potrebbe essere come segue ( stato messo il codice completo di un programma per testarne il funzionamento). [C++] bsearch() artigianale #include #include using namespace std; void* mybsearch ( const void*, const void*, size_t, size_t, int (*)(const void *, const void *) ); int compC (const void* a, const void* b) { return (*(char*)a - *(char*)b); } int compI (const void* a, const void* b) { return (*(int*)a - *(int*)b); } int main(int argc, char *argv[]) { char str[] = "abcdefghijklmno"; // Vettore di char char src = 'p'; // Non lo trover int vet[] = { 10 , 20 , 30 , 50 }; // Vettore di interi int srcI = 10; // Lo trover char* f1 = (char*)mybsearch (&src, str, strlen(str), sizeof (char), compC); int* f2 = (int*) mybsearch (&srcI, vet, 4, sizeof (int), compI); if (f1 != NULL) cout
  • system("PAUSE"); return EXIT_SUCCESS; } void* mybsearch (const void* search, const void* vet, size_t n, size_t width, int (*p)(const void *, const void *)) { size_t mezzo; size_t inf = 0; size_t sup = n - 1; while (inf
  • quindi gli aggiunge la quantit necessaria per arrivare allelemento di indice mezzo. Se avete letto la prima parte della guida, avrete capito di cosa parliamo. Ci spostiamo in pratica di mezzo*sizeof(tipo-vet) byte in memoria, raggiungendo quindi la locazione di indice mezzo. Abbiamo gi introdotto, anche se in modo informale, uno dei primi utilizzi dei puntatori a funzione: dato un algoritmo standard, possibile applicargli criteri diversi a seconda dellutilizzo che se ne vuole fare. Questi criteri si possono applicare grazie ai puntatori a funzione. Una nota importante: quando si scrive una funzione che ne accetta unaltra come argomento, bene specificare sempre, oltre ai dati intrinseci ricavabili dal prototipo del puntatore, la natura dei dati restituiti e presi come argomenti. Ad esempio, per la funzione compare() fondamentale sapere che il risultato deve essere uguale a zero in caso di uguaglianza e minore o maggiore di zero a seconda di quale sia lelemento pi grande. Il primo consiglio che vogliamo quindi fornire quindi quello di allegare alle vostre routine una piccola guida, magari scritta subito prima del prototipo o, se la fornite, allinterno dellimplementazione. Lultimo aspetto che ci rimane da analizzare quello della restituzione di un puntatore a funzione. In realt questa tecnica non molto semplice da utilizzare e ricordare, quindi bene sempre utilizzare unistruzione typedef per facilitare il compito. Osservate la sintassi da utilizzare per dichiarare una funzione che restituisce un puntatore ad una seconda funzione. tipo-punt ( *nome-funz ( arg-funz ) ) ( arg-puntatore ); Analizziamo i vari valori: tipo_punt Il valore restituito dalla funzione puntata restituita. nome-funz Nome della funzione che restituir il puntatore. arg-funz Lista dei parametri della funzione che restituir il puntatore. 22

  • arg-puntatore Lista dei tipi della funzione che viene restituita sotto forma di puntatore dalla funzione. Ecco un esempio di programma che, a seconda del carattere ricevuto, restituisce un puntatore alla funzione somma o alla funzione prodotto. [C++] Restituzione di un puntatore a funzione #include #include using namespace std; // Funzioni aritmetiche int somma (int a, int b) { return a + b; } int prodotto (int a, int b) { return a * b; } // Funzione che restituisce un puntatore a funzione int (*scelta(char))(int,int); int main(int argc, char *argv[]) { // Lettura operandi int x1, x2; cout > x1; cout > x2; // Lettura della scelta char cosa; cout > cosa; cout
  • // Funzioni aritmetiche int somma (int a, int b) { return a + b; } int prodotto (int a, int b) { return a * b; } // Definizione del puntatore typedef int (*operazione)(int,int); // Funzione che restituisce un puntatore a funzione operazione scelta (char); int main(int argc, char *argv[]) { // Lettura operandi int x1, x2; cout > x1; cout > x2; // Lettura della scelta char cosa; cout > cosa; cout
  • Abbiamo allocato il vettore in modo statico, quindi la dimensione deve essere nota gi in fase di compilazione, con tutte le regole che ne conseguono:

    dim-vettore deve essere un numero intero dim-vettore deve essere un parametro immediato o una costante dim-vettore non pu essere una variabile

    Le regole rimangono sempre quelle viste fino ad ora: durante lassegnazione si assegna ad un elemento del vettore una funzione, durante la chiamata la dereferziazione facoltativa ma consigliata. int (*p[10]) (int,int); p[0] = funz1; p[1] = funz2; for (int i = 0; i < 2; i++) cout

  • cout
  • NULL. In seguito, letta la variabile scelta, baster accedere allelemento scelta del vettore per avere la funzione desiderata. A questo punto dovreste aver familiarizzato con i puntatore a funzione e aver capito i loro principali utilizzi. Un esempio che forse vi capiter di utilizzare quello del multi-threading: in ambiente Windows, la funzione createThread(), che esegue unoperazione contemporaneamente a quella attuale, richiede, tra i parametri, proprio un puntatore alla funzione da eseguire in parallelo. Nel corso della vostra avventura di programmatori vi imbatterete sicuramente in problemi che necessitano o sono semplificati con i puntatori a funzione, quindi non dimenticate mai di avere questo potente strumento.

    27

    Puntatori a funzione

    /ColorImageDict > /JPEG2000ColorACSImageDict > /JPEG2000ColorImageDict > /AntiAliasGrayImages false /DownsampleGrayImages true /GrayImageDownsampleType /Bicubic /GrayImageResolution 300 /GrayImageDepth -1 /GrayImageDownsampleThreshold 1.50000 /EncodeGrayImages true /GrayImageFilter /DCTEncode /AutoFilterGrayImages true /GrayImageAutoFilterStrategy /JPEG /GrayACSImageDict > /GrayImageDict > /JPEG2000GrayACSImageDict > /JPEG2000GrayImageDict > /AntiAliasMonoImages false /DownsampleMonoImages true /MonoImageDownsampleType /Bicubic /MonoImageResolution 1200 /MonoImageDepth -1 /MonoImageDownsampleThreshold 1.50000 /EncodeMonoImages true /MonoImageFilter /CCITTFaxEncode /MonoImageDict > /AllowPSXObjects false /PDFX1aCheck false /PDFX3Check false /PDFXCompliantPDFOnly false /PDFXNoTrimBoxError true /PDFXTrimBoxToMediaBoxOffset [ 0.00000 0.00000 0.00000 0.00000 ] /PDFXSetBleedBoxToMediaBox true /PDFXBleedBoxToTrimBoxOffset [ 0.00000 0.00000 0.00000 0.00000 ] /PDFXOutputIntentProfile () /PDFXOutputCondition () /PDFXRegistryName (http://www.color.org) /PDFXTrapped /Unknown

    /Description >>> setdistillerparams> setpagedevice