I PUNTATORI Allocazione Statica Dato un blocco ogni variabile è allocata in memoria quando inizia l’elaborazione del blocco e deallocata quando l’elaborazione di tutto il blocco termina. Allocazione Dinamica Quando ogni variabile è allocata o deallocata in memoria durante l’elaborazione del blocco. Variabile dinamica E’ una variabile alla quale si assegna spazio in memoria durante l’elaborazione di un blocco. Il puntatore Un puntatore è una variabile il cui valore rappresenta un indirizzo di memoria. Esso serve per creare o eliminare una variabile dinamica. identificatore = ^ type ESEMPIO TYPE DataType = RECORD Giorno:1..31; Mese:1..12; Anno:integer END; DataPunt=^DateType; IntPunt=^integer; VAR Oggi:DataPunt; A,B:IntPunt; Domani:DataType; Si noti che la variabile Oggi, così come la variabile IntPunt, non assume i tre valori del record, o il valore di intero, a cui fa riferimento ma solo quello dell’indirizzo di memoria a partire dal quale vi sono eventualmente i valori Variabile Anonima: una variabile alla quale si accede solo tramite un puntatore Variabile Nominata: una variabile alla quale si accede tramite un nome Indirizzi di memoria Puntatori Nome Type Run-Time Heap 00010101 …………. …………. …………. DataPunt=^ Record Oggi Oggi^ IntPunt=^ Integer ……………... A B C A^ B^ C^ …………. ……………... 10101011 TYPE DataType = RECORD Giorno:1..31; Mese:1..12; Anno:integer END; DataPunt=^DateType; IntPunt=^integer; VAR Oggi:DataPunt; A,B:IntPunt; Domani:DataType; Memoria ……………... ……………... …………. …………. Nome Variabile ……………... Per assegnare un indirizzo a una variabile puntatore si usa la procedura new: es. new(Oggi) Spazio di memoria assegnato Prima di fare la chiamata new(Oggi) Oggi^ ? Dopo la chiamata new(Oggi) Oggi^ ? ? ? TYPE Per assegnare dei valori alla variabile dinamica Oggi new(Oggi) Oggi^ ? ? ? read(Oggi^.Giorno, Oggi^.Mese, Oggi^.Anno) Enter 21 11 2000 Oggi^ 21 .Giorno 11 .Mese 2000 .Anno DataType = RECORD Giorno:1..31; Mese:1..12; Anno:integer END; DataPunt=^DateType; IntPunt=^integer; VAR Oggi:DataPunt; A,B:IntPunt; new(A); new(B); new(Oggi); A^:=5; B^:=7; write(‘Dammi la data (giorno mese anno): ‘); WITH Oggi^ DO readln(Giorno, Mese, Anno) Dammi la data (giorno mese anno) : 21 11 2000 Oggi^ 21 11 A^ 5 B^ 7 2000 TYPE DateType = RECORD Giorno:1..31; Mese:1..12; Anno:integer END; DataPunt=^DateType; IntPunt=^integer; VAR Oggi:DataPunt; A,B:IntPunt; Gli unici operatori che si applicano alle variabili puntatori sono: operatore di assegnazione := operatori booleani = L’operazione A^ :=B^ A^ 7 5 B^ 7 C^ 12 L’operazione C^ B^ <> TYPE DataType = RECORD Giorno:1..31; Mese:1..12; Anno:integer END; DataPunt=^DateType; IntPunt=^integer; VAR Oggi:DataPunt; A,B,C:IntPunt; Domani:DataType; C :=B X 12 7 garbage A^ : =5 B^ := 7 A^ :=B^ IF (A^ = B^) AND (A <> B) THEN writeln(‘ I puntatori sono diversi ma i valori delle variabili puntate sono eguali’) A^ 7 B^ 7 TYPE IntPunt=^integer; A^ : =5 B^ := 7 A :=B IF (A^ = B^) AND (A <> B) THEN VAR A,B,C:IntPunt; writeln(‘ I puntatori sono eguali e anche i valori delle variabili puntate sono eguali’) A^ 5 B^ 7 Come eliminare la spazzatura TYPE IntPunt=^integer; VAR A,B,C:IntPunt; C :=B IF (C = B) THEN writeln(‘ I puntatori puntano alla stessa variabile’) C^ X B^ garbage 12 7 dispose(C) C :=B IF (C = B) THEN writeln(‘ I puntatori puntano alla stessa variabile’) B^ C^ 7 ? B^ C^ 7 In memoria esiste una speciale area detta run-time-heap dove sono allocate le variabili puntatore. Nello heap ci sono le variabili puntatori create da new ad es. ABCD L’istruzione C:=B mostra che possiamo assegnare memoria ad un puntatore senza fare uso di new questo ci permette di avere due puntatori che puntano alla stessa variabile dinamica, riducendo così lo spazio di memoria usato. Possiamo quindi scrivere new(B); B^:=18; C:=B Questa istruzione è valida solo se il puntatore C esiste. Vi è un solo valore che una variabile puntatore può assumere e che non punta a nulla: NIL. Es: D:= NIL; Questa assegnazione serve per informare che per ora la variabile puntatore non è stata ancora associata a una variabile dinamica. Attenzione se a un puntatore è assegnato NIL, es. D:= NIL, non è possibile fare dispose(D). Si possono fare test per vedere se il puntatore è libero di essere associato ad una variabile dinamica. Attenzione NIL non è assegnato per default. Se abbiamo allocato memoria es. new(D); D^:=5; D:= NIL resta spazzatura bisogna invece fare come di seguito new(D); D^:=5; dispose(D); D:= NIL; NIL non elimina la spazzatura PROGRAM ArrayPuntatori(input, output, Semester); CONST MaxStu=100; TotaleProve=100; TYPE Stringa4 = STRING[4]; Stringa10 = STRING[10]; Stringa25 = STRING[25]; RisultatiArray=ARRAY[1..TotaleProve] OF integer; StuRec = RECORD Cognome, Nome : Stringa25; Nascita:Stringa10; Matricola:Stringa10; AnnoCorso:Stringa4; Risultati:RisultatiArray; Media:real; END; StuFile=FILE OF StuRec; StuPointer=^StuRec; PointArr=ARRAY[0.. MaxStu] OF StuPointer; VAR Semester: StuFile; ByName, ByMat: PointArr; TotalStu:integer; Matr:Stringa10; StuRec Nome Nascita Matricola AnnoCorso Risultati Media PROBLEMA Leggere il file Semester e realizzare due array di puntatori uno ordinato per nome (ByName) ed uno per matricola (ByMat). L’array ByName contiene i puntatori agli studenti ordinati per nome. Vogliamo scrivere una funzione che faccia una ricerca di uno studente per numero di matricola sull’array ByMat. ByName 1 ByMat Abate Carlo 30/11/76 050/714 2000 21 22 23 30 27 25 Carlini Anna 30/11/72 050/734 1999 28 22 28 30 27 27 mid mid TotalStu 1 Zucchi Ugo 03/01/75 050/514 2000 30 21 23 30 27 24 TotalStu PROGRAM ArrayPuntatori(input, output, Semester); CONST MaxStu=100; TotaleProve=100; TYPE Stringa4 = STRING[4]; Stringa10 = STRING[10]; Stringa25 = STRING[25]; RisultatiArray=ARRAY[1..TotaleProve] OF integer; StuRec = RECORD StuFile=FILE OF StuRec; Cognome, StuPointer=^StuRec; Nome : Stringa25; PointArr=ARRAY[0.. MaxStu] OF StuPointer; Nascita:Stringa10; VAR Matricola:Stringa10; Semester: StuFile; AnnoCorso:Stringa4; ByName, ByMat: PointArr; Risultati:RisultatiArray; TotalStu:integer; Media:real; Matr:Stringa10; END; Indirizzi di memoria 00010101 …………. …………. …………. …………. Puntatori Nome Type StuPointer=^ StuRec Run-Time Heap Nome Variabile By Name By Name ^ ByMat ByMat ^ Memoria PointArr=^ Array OF ……………... ……………... PROCEDURE Insert(NewElement:StuPointer; Candidate: integer; VAR ByMatP:PointerArray); BEGIN WHILE (ByMatP[Candidate-1]^.Matricola > NewElement^.Matricola ) DO BEGIN ByMatP[Candidate]:= ByMatP[Candidate-1]; Candidate:=Candidate-1 END; ByMatP[Candidate]:=NewElement END; PROCEDURE OrganizzaDati(VAR ByName,ByMat:PointerArray;VAR TotalStu:integer; VAR StuOnFile:StuFile); VAR AStu:StuPointer; BEGIN reset(StuOnFile); TotalStu := 0; new(ByMat[0]); ByMat[0]^.Matricola:='00000'; WHILE NOT eof(StuOnFile) DO BEGIN new(AStu); read(StuOnFile, AStu^); TotalStu := TotalStu + 1; ByName[TotalStu] := AStu; Insert(AStu, TotalStu, ByMat) END; close(StuOnFile) END; FUNCTION CercaStudente(VAR ByMatP: PointerArray; Numero:Stringa10; Lo,Hi:integer): StuPointer; VAR J:integer; BEGIN IF Lo > Hi THEN CercaStudente :=NIL ELSE BEGIN J:=(Lo+Hi) DIV 2; IF ByMatP[J]^.Matricola = Numero THEN CercaStudente:= ByMatP[J]; ELSE IF ByMatP[J]^.Matricola < Numero THEN CercaStudente:= CercaStudente(ByMat, Numero, J+1,Hi) ELSE CercaStudente:= CercaStudente(ByMat, Numero, Lo,J-1) END END; PROCEDURE Risultati(MatrCand:Stupointer); BEGIN writeln(Matrcand^.Cognome,' ', MatrCand^.Nome,' ', MatrCand^.Matricola); END; {******************** MAIN ******************} BEGIN assign(semester,'a:\probe.dat'); OrganizzaDati(ByName,ByMat, TotalStu, Semester); readln; write('Dammi la matricola cercata: '); readln(Matr); Risultati(CercaStudente(ByMat,Matr,1,TotalStu)); readln END. Esercizio Scrivere il programma completo per la gestione dei records attraverso array di puntatori ordinati per Nome, Matricola, Data di Nascita, Media. Alcuni suggerimenti sui puntatori Per il passaggio di parametri relativi ai puntatori valgono le stesse regole che si applicano per gli altri tipi di variabili. Quando un puntatore è chiamato per valore viene fatta una copia locale del suo valore, cioè dell’indirizzo. Quando un puntatore è chiamato per variabile viene prodotto un alias locale per il suo valore attuale, ricordando sempre che si tratta di indirizzi. PROGRAM TestChiamatePuntatori; TYPE IntP=^integer; VAR AnIntP:IntP; PROCEDURE ValCall(XP:IntP); BEGIN XP^:=7; END; PROCEDURE VarCall(VAR XP:IntP); BEGIN BEGIN new(AnIntP); XP^:=7; BEGIN AnIntP^:=5; END; new(AnIntP); ValCall(AnIntP); AnIntP^:=5; writeln(‘Output= ‘,AnIntp^:1); ValCallVar(AnIntP); END. writeln(‘Output= ‘,AnIntp^:1); END. Output= 7 Questo accade perché l’indirizzo passato dalla chiamata rimane lo stesso mentre il valore della variabile dinamica è cambiato. Questa chiamata equivale ad una chiamata per VAR su XP^. Output= 7 Il valore resta lo stesso essendo la chiamata precedente equivalente ad una chiamata per VAR su XP^. PROGRAM TestChiamatePuntatori; TYPE IntP=^integer; VAR AnIntP:IntP; PROCEDURE ValCall(XP:IntP); BEGIN dispose(XP); END; PROCEDURE VarCall (VAR XP:IntP); BEGIN dispose(XP); BEGIN END; new(AnIntP); BEGIN new(AnIntP); AnIntP^:=5; ValCall(AnIntP); writeln(‘Output= ‘,AnIntp^:1); END. Output= non predicibile Questo accade perché l’indirizzo passato dalla chiamata viene deallocato. Poiché questo indirizzo nel main corrisponde anche a quello di AnInt^ il valore di AnInt^ ora non è più predicibile. AnIntP^:=5; VarCall (AnIntP); writeln(‘Output= ‘,AnIntp^:1); END. Output= non predicibile Come prima. PROGRAM TestChiamatePuntatori; TYPE IntP=^integer; VAR AnIntP:IntP; PROCEDURE ValCall(XP:IntP); BEGIN dispose(XP); new(XP); END; PROCEDURE VarCall (VAR XP:IntP); BEGIN BEGIN BEGIN new(AnIntP); dispose(XP); new(AnIntP); AnIntP^:=5; new(XP); AnIntP^:=5; ValCall(AnIntP); END; VarCall (AnIntP); writeln(‘Output= ‘,AnIntp^:1); writeln(‘Output= ‘,AnIntp^:1); END. END. Output= non predicibile Questo accade perché l’indirizzo passato Output= non predicibile dalla chiamata viene deallocato. Poiché Come prima. Solo che ora non si questo indirizzo nel main corrisponde crea spazzatura. anche a quello di AnInt^ il valore di AnInt^ ora non è più predicibile. Inoltre la memoria allocata da new diventa spazzatura non appena si esce dal blocco. PROGRAM TestChiamatePuntatori; TYPE IntP=^integer; VAR AnIntP:IntP; PROCEDURE ValCall(XP:IntP); BEGIN XP:=NIL; END; PROCEDURE VarCall (VAR XP:IntP); BEGIN BEGIN XP:=NIL; BEGIN new(AnIntP); END; new(AnIntP); AnIntP^:=5; AnIntP^:=5; ValCall(AnIntP); VarCall (AnIntP); writeln(‘Output= ‘,AnIntp^:1); writeln(‘Output= ‘,AnIntp^:1); END. END. Output= 5 Questa istruzione riassegna un valore a XP. Ora XP e AnInt hanno valori differenti. Quando si esce da ValCall XP è perso e quindi AnInt resta =5. Non c’è spazzatura perché abbiamo usato il NIL. Output= non predicibile Questa istruzione riassegna a AnIntP il valore NIL. Quindi quando si esce da VarCall AnIntP^ non è predicibile inoltre a AnIntP è stato assegnato un nuovo valore per cui il precedente contenente 5 è diventato spazzatura. PROGRAM TestChiamatePuntatori; TYPE IntP=^integer; VAR AnIntP:IntP; PROCEDURE ValCall(XP:IntP); BEGIN new(XP); XP^:=7 writeln(‘OutputCall ‘,XP^:1) END; BEGIN PROCEDURE VarCall (VAR XP:IntP); new(AnIntP); BEGIN BEGIN AnIntP^:=5; new(XP); new(AnIntP); ValCall(AnIntP); XP^:=7 AnIntP^:=5; writeln(‘Output= ‘,AnIntp^:1); writeln (‘OutputCall ‘, XP^:1) VarCall (AnIntP); END. END; writeln(‘Output= ‘,AnIntp^:1); OutputCall=7 END. Output= 5 OutputCall=7 Qui si alloca un nuovo indirizzo per la variabile Output= 7 che prima aveva indirizzo AnInt. L’effetto della L’istruzione new(XP) fa sì che AnIntP assegnazione XP:=7 è ristretto solo agli scopi diventa spazzatura. Quindi il valore 7 è della procedura ValCall. mostrato due volte poiché tramite la Ora XP e AnIntP rappresentano due diverse Call VAR restituisce detto valore a variabili puntatore, quando usciamo dalla AnIntp^. procedura AnIntP^ vale sempre 5, mentre il valore di XP^ è perduto come spazzatura. ESERCIZI SULLA RICORSIONE Sia data una stringa di lunghezza N e una sottostringa di lunghezza K con K sottomultiplo di N. Scrivere una funzione ricorsiva che conti tutte le sottostringhe di N che non sono uguali a K. PROGRAM ESERCIZIO3 (input, output); TYPE String30=STRING[30]; VAR Carat:char; S1: String30; S2: String30; M1,M2,Numstr,C:integer; {*********** MAIN *********} BEGIN S1:='ababababa'; S2:='aba'; M1:=length(S1); M2:=length(S2); writeln('Prima stringa ',S1); writeln('Seconda stringa ',S2); NumStr:=Cerca(S1,S2,M1,M1,M2,C); Writeln(' Diverse ',Numstr,' Uguali ',C:5); FUNCTION Cerca(VAR S1,S2:String30;i,L1,L2:integer):integer; BEGIN IF (i=length(S2)-1) THEN Cerca:=0 ELSE IF (L2=0) THEN BEGIN Cerca:=Cerca(S1,S2,i-1,i-1,length(S2)); END ELSE IF S1[L1]=S2[L2] THEN Cerca:=Cerca(S1,S2,i,L1-1,L2-1) ELSE Cerca:=Cerca(S1,S2,i-1,i-1,length(S2))+1 END; Sia data la successione: a0=0 a1=1 a2=1 a3=2 an= an-1+ an-2* an-3 - an-4 Scrivere una PROCEDURA ricorsiva che, assegnato un N, calcoli il numero K dato da: K=an+1 *an-1 - 2*a2n {MAIN} BEGIN PROGRAM Esercizio4(output); write(' Dammi N ');readln(No); CONST MaxSucc=50; SetSuccNo(No+1,SuccNos); TYPE K:=SuccNos[No+1]*SuccNos[No-1] SuccNoArr=ARRAY[0..MaxSucc] OF real; -2*SuccNos[No]* SuccNos[No]; VAR writeln; SuccNos:SuccNoArr; writeln('K= ',K:20:0); VAR readln No,I:integer; END. K:real; PROCEDURE SetSuccNo(N:integer; VAR SuccNos:SuccNoArr); {ritorna l'N-esimo valore della successione} BEGIN IF N=3 THEN BEGIN SuccNos[3]:=2; SuccNos[2]:=1; SuccNos[1]:=1; SuccNos[0]:=0; END ELSE BEGIN SetSuccNo(N-1,SuccNos); SuccNos[N]:=SuccNos[N-1]+SuccNos[N-2]*SuccNos[N-3]-SuccNos[N-4]; END END;