1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo...

32
1

Transcript of 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo...

Page 1: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

1

Page 2: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

2

Puntatori e function

Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo di partenza del suo codice.

Un puntatore a function contiene l’indirizzo di memoria della function; esso può essere passato o restituito da un’altra function e può essere assegnato ad un altro puntatore a function.

Il puntatore a function è molto utile quando è necessario, nel corso di un programma, scegliere tra più function.

STACK Stack: zona di memoria che costituisce un’area di appoggio temporaneo

CODICE Codice: contiene il codice macchina del programma

HEAP Heap: zona di memoria libera di essere utilizzata sul momento

DATI Dati: contiene tutte le variabili e le costanti definite nel programma

Page 3: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

3

STACK Stack: zona di memoria che costituisce un’area di appoggio temporaneo

CODICE Codice: contiene il codice macchina del programma

HEAP Heap: zona di memoria libera di essere utilizzata sul momento

DATI Dati: contiene tutte le variabili e le costanti definite nel programma

Puntatori e function

Supponiamo di avere due function, funz1 e funz2 del tipoint funz1(char) e int funz2(char)

ed un’altra function funzvoid funz(int, int)

A seconda delle varie opportunità, ci si vuole servire di funz1 o di funz2: il primo intero int può essere il risultato della prima (funz1), il secondo di funz2 o viceversa. Come possiamo scrivere il codice in maniera tale che la function funz possa richiamare l’una o l’altra tra funz1 o funz2?

Page 4: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

4

Utilizzando i puntatori a function possiamo scrivere il prototipo di funz in questo modo:

void funz(int (*)(char), int)

il primo parametro int da assegnare alla function deve essere il risultato di una function che ha un parametro char (potrebbe essere funz1 o funz2 o qualsiasi altra function che ha le stesse caratteristiche: stessa tipologia di parametri formali e stesso tipo di valore restituito).

L’implementazione della function funz deve avere un’intestazione del tipo

void funz(int (*puntafunz)(char), int n)

mentre la function funz può essere richiamata con una delle seguenti modalità

funz(funz1, m) funz(funz2, m)

Page 5: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

5

ESEMPIO

Un esempio di utilizzo dei puntatori a function concerne l’ordinamento.

Supponiamo di voler ordinare un array A di interi. Possiamo decidere di ordinarli sia in modo crescente che decrescente.

A questo scopo supponiamo di avere due function, chiamate crescente e decrescente, che restituiscono un valore booleano.

Come organizzare il programma in modo tale da passare la function che serve allo scopo?

Il puntatore a function risolve il problema.

Page 6: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

6

Di seguito è riportato il codice che permette di scegliere fra il realizzare un ordinamento crescente o decrescente dei dati contenuti in un array di interi.

Le function interessate sono:

void ordinaB (int vet[], int N, bool (*confronta)(int,int))

bool crescente(int x, int y)

bool decrescente(int x, int y)

void scambia (int &x1, int &x2)

Page 7: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

7

#include <iostream>#include <cstdlib>#include "InsertArray.h"using namespace std;

// PROTOTIPI

void ordinaB(int[ ], int, bool(*)(int,int));bool crescente(int, int);bool decrescente(int, int); void scambia (int&, int&);

int main () { char ch; int a[100], n; cout<<" Lunghezza vettore="; cin>>n; LeggeVettore ( a, n, 'a'); do { cout<<" CRESCENTE "<<endl; ordinaB(a,n,crescente); StampaVettore (a, n,'a'); cout<<"\n DECRESCENTE "<<endl; ordinaB(a,n,decrescente); StampaVettore (a, n,'a');

cout<<"\n f per terminare ";cin>>ch;

} while (ch!='f'); return 0; }

Page 8: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

8

// implementazione delle procedure e functionvoid scambia (int &x1, int &x2) { int s; s=x1; x1=x2; x2=s;}void ordinaB (int vet[], int N, bool (*confronta)(int,int)) { int j, k; for (k=0; k<N-1; k++) for (j=k+1; j<N; j++) if ((confronta)(vet[k], vet[j]))

{ scambia (vet[k],vet[j]);

} } bool crescente(int x, int y) { return (x>y); }bool decrescente(int x, int y) { return (x<y); }

Allegato: funzPuntatori

Page 9: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

9

Esercizio

Con i puntatori a funzione scrivere una function che valuti il valore massimo contenuto in un vettore di interi, il minimo e la somma di tutti gli elementi del vettore.

Con i puntatori a funzione scrivere una function che verifichi se assegnata una matrice di interi essa è unitaria, oppure simmetrica oppure con elementi tutti non negativi.

funzMinMax.cpp

funzMatrixPunt

Page 10: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

10

Variabili dinamiche

Nel caso delle variabili statiche la memoria viene allocata nel momento in cui la variabile è definita; per esempio le variabili

int x, a[10];impegnano la memoria nel momento stesso in cui vengono definite, mentre vengono deallocate nel momento in cui il programma termina, se la variabile è globale; se, invece, la variabile è locale (cioé definita in una function) allora viene deallocata quando termina la function.

In molti casi, però, è più comodo avere un controllo completo sulla allocazione di memoria cioè decidere quando allocare o deallocare la variabile in un qualsiasi punto del programma.

Page 11: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

11

Questo è il concetto chiave di gestione dinamica della memoria.

Il C++ mette a disposizione del programmatore due istruzioni:

new che serve per allocare memoria per una certa variabile durante l’esecuzione del programma

delete che serve a deallocare memoria (libera la zona di memoria occupata dall’oggetto definito con new).

Page 12: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

12

Volendo definire due variabili dinamiche, ad esempio un intero ed un array di 100 interi, si procede in questo modo:

si definiscono due puntatori ad interi, int *P1, *P2;

si usa l’operatore new per indicare che è una variabile dinamica

sia per l’intero P1=new int

che per l’arrayP2=new int [100]

Se l’operatore new fallisce, ritorna il puntatore nullo NULL: ciò indica che, per qualche ragione, non è stato possibile allocare altra memoria.

Page 13: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

13

Il programma può utilizzare i puntatori P1 e P2 nei suoi

calcoli; può, inoltre, deallocarli in un qualsiasi momento

con l’istruzione delete:

delete P1; // cancella dalla memoria heap l’intero di cui P1 contiene l’indirizzo

delete [ ] P2; // cancella dalla memoria heap l’array di cui P2 contiene l’indirizzo-base

Vediamo l’utilizzo delle variabili dinamiche per un algoritmo di selection sort

Page 14: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

14

#include <iostream>#include <cstdlib>#include "InsertArraypunt.h"using namespace std;// PROTOTIPIvoid SortSel(int*, const int);void Scambia (int*, int*);// questa function usa puntatori

che puntano ad interi NON ad array

// MAIN int main() { int N, *ap; //viene definito un puntatore ad un array ap=new int[30]; // ap è un puntatore ad un array: è

una variabile dinamica con al più 30 interi if (ap==NULL) { //se ap è NULL allora non è possibile

allocare memoria cout<<"Non è possibile allocare memoria"<<endl; return -1; } //il programma termina cout<<”Numero valori da caricare in a ="; cin>>N; LeggeVettore(ap,N,'a'); // in questa e nelle chiamate

successive si passa il puntatore ap all'array SortSel(ap,N); StampaVettore(ap,N,'a'); delete [ ] ap; //si libera la memoria a cui punta ap system("pause"); }

ESEMPIO

Dal main si evince che viene creato lo spazio e caricato un array con N interi (Legge Vettore), successivamente vengono ordinati (SortSel) e stampati (StampaVettore) i dati e quindi viene liberato lo spazio di memoria

Page 15: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

15

// DEFINIZIONIvoid SortSel(int *vet, const int Nv) { int minp; for ( int i=0; i<Nv-1; i++) { minp=i; for ( int j=i+1; j<Nv; j++) { if (*(vet+j) < *(vet+minp)) minp=j; } Scambia((vet+minp), (vet+i)); // Attenzione! La procedura Scambia richiede puntatori!! } }

void Scambia (int *x, int *y) { int z; z=*x; *x=*y; *y=z; }

Allegato: SortPuntatori2

ESEMPIO

Si noti che gli elementi del vettore vengono individuati tramite i puntatori.

Page 16: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

16

Esercizi

Assegnato un array A scrivere una funzione booleana ricorsiva che, operando con i puntatori, determini se i valori disposti nell’array sono simmetrici rispetto al centro.

Assegnato un array A scrivere una procedura ricorsiva che, operando con i puntatori, ordini l’array con l’algoritmo dell’inserction sort.

6 8 7 8 9

Page 17: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

17

/*Assegnato un array A[10] scrivere una funzione booleana ricorsiva che, operando con i puntatori, determini se i valori disposti nell’array sono simmetrici rispetto al centro. */#include <iostream>#include <cstdlib>#include "InsertArraypunt.h"

using namespace std;//void StampaVettore(ap,N,'a');bool simm(int*,int,int); int main(){ int *array; int *lng; array=new int[50]; lng= new int; cout<<" Numero dati= ";cin>>*lng;cout<<"\n"<<endl; for(int i=0;i<*lng;i++) { cout<<"A["<<i<<"]="; cin>>array[i]; } cout<<"\n CONTROLLO...\n"<<endl; StampaVettore(array,*lng,'a'); if (simm(array,*lng-1,0)) cout<<"\n L'ARRAY E' SIMMETRICO\n"<<endl; else cout<<"\n L'ARRAY NON E' SIMMETRICO\n"<<endl; system("pause");}

arraySimmetrico

bool simm (int *array,int lng, int pos){ if(lng <= pos) return true; else { if(array[lng]==array[pos]) return simm (array,lng-1,pos+1); else return false; }}

Page 18: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

18

Consideriamo il tipo

struct TPersona {char cognome[20];char nome[20];Tdata nascita;char luogo[20];double reddito;}

Definiamo due variabili

TPersona* puntP;

TPersona persona;

La variabile puntP non ha un nome in quanto il suo nome coincide con l’indirizzo;Le variabili di questo tipo sono dette anonime; il loro contenuto può essere perso se non ne conosciamo più l’indirizzo.

I puntatori e i record

STACK

CODICE

HEAP

DATI

Page 19: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

19

Per memorizzare il contenuto cui punta puntP dobbiamo:

Usare new per allocare lo spazio nello HeappuntP=new Tpersona

Inserire i daticout<<“Cognome=“; cin>>puntP.cognome;

cout<<“Nome=“; cin>>puntP.nome;

cout<<“Data Nascita=“; cin>>puntP.nascita.giorno >>puntP.nascita.mese >>puntP.nascita.anno;

cout<<“Luogo Nascita=“; cin>>puntP.luogo;

cout<<“Reddito=“; cin>>puntP.reddito;

Una volta inseriti i dati questi si trovano nella memoria heap a cui è possibile accedere solo attraverso puntP e l’operatore “.”

Page 20: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

20

AbateLuca21 9 1976Napoli12000

ZurloAldo1 8 1966Milano37000

puntP puntP1

Supponiamo siano assegnate due variabili

Se poniamo puntP1=puntP si avrà

AbateLuca21 9 1976Napoli12000

puntP

puntP1

La zona di memoria a cui puntavapuntP1 resta occupata ma irraggiungibilein quanto ne abbiamo perso l’indirizzo.

Si crea così garbage o spazzatura e nonserve a nulla porre puntP1=NULL

Page 21: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

21

Passaggio dei puntatori ad una function.

Tenendo presente che il puntatore è una variabile che

fornisce l’indirizzo di un’altra variabile, analizziamo alcune

situazioni-tipo.

Abbiamo un programma principale contenente due

puntatori ad un intero; esso richiama due procedure: nella

prima la variabile intera è passata per valore, nella

seconda per riferimento.

Il programma principale è lo stesso, mentre faremo variare

le due procedure.

Page 22: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

22

Di seguito si mostra il codice e l’output del programma che opera chiamate dei puntatori sia per valore che per riferimento.

#include <iostream>#include <cstdlib>using namespace std;void CallVal(int*);void CallRif(int* &);int main () { int *p1, *p2; p1=new int; p2=new int; *p1=5; *p2=5;

CallVal(p1); CallRif(p2); system("pause");}

Page 23: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

23

int main () { int *p1, *p2; p1=new int; p2=new int; *p1=5; *p2=5; cout<<"indirizzo p1 pre-CalVal "<<p1<<" valore "<<*p1<<endl<<endl; CallVal(p1); cout<<"indirizzo p1 post-CalVal "<<p1<<" valore "<<*p1<<endl<<endl; cout<<"indirizzo p2 pre-CalRif "<<p2<<" valore "<<*p2<<endl<<endl; CallRif(p2); cout<<"indirizzo p2 post-CalRif "<<p2<<" valore "<<*p2<<endl<<endl;

}void CallVal(int* xp) { xp=new int; *xp=7; cout<<"indirizzo p1 in-CalVal "<<xp<<" valore "<<*xp<<endl<<endl;

}void CallRif(int* &xp) { xp=new int; *xp=7; cout<<"indirizzo p2 in-CalRif "<<xp<<" valore "<<*xp<<endl<<endl;

}

Page 24: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

24

int main () { int *p1, *p2; p1=new int; p2=new int; *p1=5; *p2=5;

CallVal(p1);

CallRif(p2);

}

Allegato: funzDinamPuntatori 3

// indirizzo p1 pre-CalVal 0x3d2490 valore 5

void CallVal(int* xp) { xp=new int; *xp=7;

}

// indirizzo p1 in CalVal 0x3d2508 valore 7

// indirizzo p1 post-CalVal 0x3d2490 valore 5

// indirizzo p2 pre-CalRif 0x3d24f8 valore 5

void CallRif(int* &xp) { xp=new int; *xp=7; }

// indirizzo p2 in CalRif 0x3d2518 valore 7

// indirizzo p2 post-CalRif 0x3d2518 valore 7

Page 25: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

25

Nell’esempio che segue il programma va in errore perché

la procedura CallRif(p2) restituisce un puntatore

p2=NULL che quindi non può essere stampato come

richiesto.

Page 26: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

26

// NON FUNZIONA

int main () { int *p1, *p2; p1=new int; p2=new int; *p1=5; *p2=5; cout<<"indirizzo p1 pre-CalVal "<<p1<<" valore "<<*p1<<endl<<endl; CallVal(p1); cout<<"indirizzo p1 post-CalVal "<<p1<<" valore "<<*p1<<endl<<endl; cout<<"indirizzo p2 pre-CalRif "<<p2<<" valore "<<*p2<<endl<<endl; CallRif(p2);// restituisce un errore perché in uscita p2 punta a NULL!! cout<<"indirizzo p2 post-CalRif "<<p2<<" valore "<<*p2<<endl<<endl; system("pause");}void CallVal(int* xp) { xp=NULL; }

void CallRif(int* &xp) { xp=NULL; }

Allegato: funzDinamPunt2

Page 27: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

struct Tpdata { int giorno; int mese; int anno; };struct Tpersona { char cognome[30]; char nome[20]; Tpdata nascita; char luogo[20]; }; typedef Tpersona* Ppersona;

//Ppersona è un nuovo tipo: puntatore a Tpersonaconst char Nomefile[]="persone.dat";

Assegnato un Array contenete dati anagrafici mostrare a video i dati ordinati per cognome, luogo di nascita e data di nascita.

// PROTOTIPI

void Stampa(Ppersona[],int); void bubble(Ppersona [] ,int,int(*)(Ppersona ,Ppersona)); int cognome(Ppersona,Ppersona); int luogo(Ppersona,Ppersona); int nascita(Ppersona ,Ppersona );

Page 28: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

main () { Tpersona persona1; Ppersona PuntaP1[101], PuntaP2[101], PuntaP3[101]; const int Lrec=sizeof(Tpersona); fstream filepers; filepers.open(Nomefile,ios::in|ios::out|ios::binary|ios::ate); int NumPers; if (!filepers) { filepers.open("persone.dat",ios::out|ios::binary|ios::trunc);

return 1;} NumPers=filepers.tellg()/Lrec; cout<<NumPers; filepers.seekg(0,ios::beg); for (int i=0; i<NumPers; i++) { //leggi I dati della persona filepers.read((char*) &persona1, Lrec); PuntaP1[i] = new Tpersona; // Inserisci i dati letti in PuntaP1[i] *PuntaP1[i] = persona1; PuntaP2[i] = PuntaP1[i]; PuntaP3[i] = PuntaP1[i]; }

Page 29: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

cout<<" STAMPA DATI "<<endl<<endl<<endl;

cout<<" INIZIALE"<<endl; Stampa(PuntaP1,NumPers);

cout<<"\n COGNOME"<<endl; bubble(PuntaP1,NumPers,cognome); Stampa(PuntaP1,NumPers);

cout<<"\n LUOGO"<<endl; bubble(PuntaP2,NumPers,luogo); Stampa(PuntaP2,NumPers);

cout<<"\n NASCITA"<<endl; bubble(PuntaP3,NumPers,nascita); Stampa(PuntaP3,NumPers);

system("pause"); return 0; }

Page 30: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

void bubble(Ppersona vet[],int N,int(*confronta)(Ppersona ,Ppersona)) { int j, k; Ppersona s; for (k=0; k<N-1; k++) { for (j=N-2; j>=k; j--) {if (confronta(vet[j], vet[j+1])>=1) { s=vet[j]; vet[j]=vet[j+1]; vet[j+1]=s; } } } }

int cognome(Ppersona Px , Ppersona Py) { return ((strcmp(Px->cognome, Py->cognome))); } int luogo(Ppersona Px , Ppersona Py){return ((strcmp(Px->luogo, Py->luogo)));} int nascita(Ppersona Px , Ppersona Py) { double data1,data2; data1=(Px->nascita.anno)*10000+(Px->nascita.mese)*100+(Px->nascita.giorno); data2=Py->nascita.anno*10000+Py->nascita.mese*100+Py->nascita.giorno; return (data1-data2);}

Page 31: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

int cognome(Ppersona Px , Ppersona Py) { return ((strcmp(Px->cognome, Py->cognome))); } int luogo(Ppersona Px , Ppersona Py){return ((strcmp(Px->luogo, Py->luogo)));} int nascita(Ppersona Px , Ppersona Py) { double data1,data2; data1=(Px->nascita.anno)*10000+(Px->nascita.mese)*100+(Px->nascita.giorno); data2=Py->nascita.anno*10000+Py->nascita.mese*100+Py->nascita.giorno; return (data1-data2);}

ArrayPuntRec3

Page 32: 1. 2 Puntatori e function Così come il nome di un array rappresenta l’indirizzo del suo primo elemento, il nome di una function rappresenta l’indirizzo.

32

ESERCIZIOSia K un intero positivo ed R una matrice mxn di interi. Si scriva una funzione ricorsiva che stabilisca se R contenga al suo interno una serie di almeno K elementi consecutivi allineati in uno qualsiasi dei tre versi orizzontale, verticale oppure diagonale. Utilizzare i puntatori.Esempio per K=3

R=

1 5 12 4 51

33 3 65 14 68

7 9 7 12 28

4 34 42 28 47

75 21 28 61 9

8 51 27 21 64