Dispense di Pascal.

Materie:Appunti
Categoria:Informatica

Voto:

1.5 (2)
Download:223
Data:01.08.2000
Numero di pagine:20
Formato di file:.doc (Microsoft Word)
Download   Anteprima
dispense-pascal_1.zip (Dimensione: 50.99 Kb)
readme.txt     59 Bytes
trucheck.it_dispense-di-pascal.doc     185 Kb


Testo

TAMC a.a. 1999-00
Dispense di PASCAL
dott. Antonella Carbonaro
0. Introduzione
Il Pascal è un linguaggio algoritmico per la programmazione sequenziale progettato nel 1968 da N. Wirth.
Carartteristiche:
• compatto con formato vincolato
• semantica degli statements ben specificata
• numero di statements limitato ma completo
• utile collezione di predefined data types
• meccanismo di subroutine generale
Il Pascal è un linguaggio compilato. Il processo di compilazione è composto da due fasi:
• l’analisi (lessicale, sintattica, semantica)
• la sintesi (linguaggio intermedio, ottimizzazione, linguaggio target).
La versione standard del Pascal: ISO, non è legata ad alcun tipo specifico di compilatore.
Il linguaggio è definito tramite il Vocabolario e la Grammatica.
La Grammatica è un insieme composto da:
• simboli terminali (parole che appartengono al vocabolario),
• simboli non terminali (metasimboli per giungere allo scopo),
• produzioni (elenco delle sostituzioni ammesse),
• scopo.
1. Vocabolario
Vocabolario
def. L’INSIEME DEI CARATTERI DI BASE utilizzabili nel descrivere un programma Pascal.
Essi sono:
• Le 26 lettere dell’alfabeto inglese (maiuscole A..Z e minuscole a..z)
• le 10 cifre arabe (da 0 a 9)
• i caratteri speciali: + - * / = < > . , : ; ‘ > ^ ( )
• i caratteri blank, tab e quelli di fine linea
2. Lessico
Unità lessicale
def. una sequenza finita dei caratteri dell’alfabeto
La Sintassi del Pascal ci dice quali SEQUENZE di unità lessicali sono riconosciute come frasi corrette.
Il linguaggio Pascal riconosce 4 tipi di unità lessicali:
1. identificatori
2. simboli speciali
3. numeri
4. stringhe
2.1. Identificatori
Sono nomi utilizzati per denotare diverse entità del linguaggio.
Sono identificatori corretti:
ciao CIAO ciAO4 c4iAo
mentre non lo sono
13maggio 66 alfa-beta c&ao $ciao £$
Nota Un identificatore può essere di una lunghezza qualsiasi di caratteri (comunque della lunghezza di una linea) TUTTAVIA solo i primi N sono considerati significativi.
Es. Se N = 8
variabile1 e variabile2 saranno considerate come lo stesso identificatore
Alcuni identificatori hanno in Pascal un significato preciso e congelato a prescindere dal quale non sono utilizzabili correttamente.
Essi sono le PAROLE CHIAVE o RISERVATE
and array begin case const div do downto
else end file for function goto if in
label mod nil not of or packed procedure
program record repeat set then to type until
var while with
Oss. Nelle carte sintattiche appaiono come simboli terminali
Sono soggette a regole sintattiche particolari (es. then deve seguire if..)
Inoltre vi sono anche gli IDENTIFICATORI STANDARD
Essi hanno un significato preciso ma non congelato (cioè possono essere ridefiniti)
e rappresentrano funzioni matematiche, valori logici, procedure standard, costanti, strutture di dati...
abs arctan boolean char chr cos dispose eof
eoln exp false get input integer ln maxint
new odd ord output pack page pred put
read readln real reset rewrite round sin sqr
sqrt succ text true trunc unpack write writeln
2.2 Simboli speciali
Sono caratteri speciali o coppie degli stessi interpretatie come singole unità.
Essi denotano operazioni o punteggiatura.
E = ^ :=
, ; : ‘ . ..
( ) ( (. .) (* *)
Nelle carte sintattiche sono ovviamente rappresentati come simboli terminali.
2.3 Numeri
Sono numeri corretti:
1 +1 -1 1.3 +1.3 -1.3 1.3E4 +1E-4 1.3E-4
2.4 Stringhe di caratteri
Una stringa è una qualsiasi sequenza di caratteri delimitata da ‘
Per scrivere il carattere ‘ basterà raddoppiarlo ’’
sono stringhe corrette:
‘12345’ ‘a’ ‘£$%秒 ‘l’’ape’ ‘’’’(l’’l
Nota:
• Il “blank” è ammesso all’interno di una stringa
• I caratteri di “fine linea” non sono permessi all’interno della stringa.
Segue che la lunghezza di una stringa non può superare la dimensione fisica di una linea.
3 Struttura di un programma Pascal
Un programma Pascal non è altro che la successione di identificatori, numeri, stringhe e caratteri speciali giustapposti secondo le regole che verranno via via specificate.
Supponiamo di voler descrivere un programma che trasferisce un testo riga per riga da un dispositivo di input (terminale) ad uno di output (stampante).
Seguendo l’approccio top-down
• ad un primo livello specifichiamo:
tre risorse su cui basare l’algoritmo risolutivo
descrizione della riga (dato su cui si opera)
descrizione di leggi
descrizione di scrivi
l’algoritmo risolutivo
ripeti fino alla fine del testo
leggi una riga (moduli
scrivi una riga
• ad un secondo livello siamo interessati a risolvere i sottoproblemi di lettura e scrittura e individuiamo:
le risorse
descrizione del carattere (dato su cui si opera)
descrizione di leggicarattere
l’algoritmo risolutivo
ripeti fino al carattere di fine-linea
leggi un carattere
Come si può notare la descrizione del modulo è identica ad ogni livello di dettaglio
e si compone di due parti
1. la specifica delle risorse utilizzate
2. l’elaborazione delle stesse.
Un programma Pascal avrà dunque la seguente sintassi:
programma=program header+blocco .
Dove:
• il program header specifica il nome del programma e i suoi rapporti con l’esterno tramite degli identificatori.
• il blocco invece include le specifiche delle risorse e dell’elaborazione delle stesse.
4 Descrizione delle risorse
Tutte le risorse utilizzate nella parte algoritmica di ogni modulo devono essere precedentemente specificate, ovvero dichiarate.
Ad ogni risorsa verrà associato un identificatore che la denoterà univocamente all’interno del programma e che sarà appunto utilizzato per riferirla nella parte algoritmica.
Le risorse possono essere di diversi tipi:
• etichette
• costanti
• tipi
• variabili
• procedure o funzioni
4.1 Dichiarazione delle etichette
Le etichette o label servono per individuare punti particolari del programma a cui ci si potrà riferire per fare un salto all’interno dello stesso.
4.2 Dichiarazione delle costanti
All’interno di un programma Pascal si possono identificatori come sinonimi di dati che non si modificano mai durante l’esecuzione del programma.
Due i vantaggi principali:
1. Migliore leggibilità del programma
2. Migliore manutenibilità: per modificare il valore, basterà modificare la sola dichiarazione anziché ogni sua occorrenza.
Il tipo della costante è determinato dal suo contenuto.
Sono dunque costanti corrette
const
max = +527; (integer)
pigreco =3.14; (real)
nome = ‘elena’; (array of char)
min = - max; (integer)
alwaystrue = true; (boolean)
a = ‘z’; (char)
4.3 Dichiarazione dei tipi
Il tipo dei dati definisce l’insieme dei valori e l’insieme delle operazioni che si possono effettuare su di essi.
Una variabile definita nel programma deve appartenere obbligatoriamente ad un solo tipo di dato.
I vantaggi sono:
• posso esprimere i dati ad un certo livello di ASTRAZIONE senza entrare nei dettagli della rappresentazione in memoria,
• la manipolazione è riferita alla STRUTTURA ASTRATTA e non alla ORGANIZZAZIONE FISICA,
• esiste protezione automatica da combinazioni errate di dati.
Definisco un TIPO DI DATO ASTRATTO attraverso :
• la struttura fisica cioè i VALORI PERMESSI (correlati con lo spazio fisico di memoria),
• gli accessi, cioè gli OPERATORI (procedure e funzioni) definite sulla struttura.
Il Pascal definisce diversi tipi di dati predefiniti e i costrutti che permettono al programmatore di definire i propri a seconda delle esigenze dettate dalla natura del problema.

BOOLEAN ENUMERATIVI ARRAY
INTEGER SUBRANGE RECORD
REAL SET
CHAR FILE
POINTER
Sui tipi torneremo più avanti.
4.4 Dichiarazione delle variabili
Se la dichiarazione del tipo introduce un sinonimo per un modello di dato, la dichiarazione della variabile esplicita un istanziamento di tale modello.
La dichiarazione associa un nome simbolico ad una variabile di un certo tipo. Pascal è fortemente tipato: le variabili devono essere dichiarate prima di essere utilizzate.
Scopo di una dichiarazione:
• definire ed allocare lo spazio di memoria necessario,
• definire i valori ammessi,
• definire gli operatori.
var
max : integer;
flag : boolean;
radice1, radice2 : real;
4.5 Dichiarazione di procedure e funzioni
Procedure e funzioni rappresentano in pascal dei veri e propri sottoprogrammi.
Nel pascal standard esse devono fare parte integrante del programma globale e costituire quindi una parte di un’unica unità di compilazione. Esistono tuttavia altri linguaggi che accettano una compilazione separata con la possibilità quindi di costruire librerie di sotto-programmi.
Distinguiamo tra procedure e funzioni perché le seconde restituiscono esse stesse un valore assegnabile ad esempio ad una variabile.

5 I tipi
Tipo di dato
def. classe dei valori e insieme delle operazioni permesse su tali elementi.
In Pascal ad ogni variabile e costante, nonche funzione, deve essere associato un tipo di dato.
Tale associazione è STATICA ed è effettuata al momento della dichiarazione o definizione dell’oggetto.
Ad esempio:
const
max=120;
var sono oggetti di tipo integer
i : integer;
5.1 Tipi non strutturati
Tipo non strutturato
def. E’ un tipo caratterizzato da un insieme di valori elementari che rappresentano atomi che non possono essere ulteriormente scomponibili.
tipo semplice: tipo enumerativo o tipo subrange o tipo predefinito
Tutto il calcolo esprimibile in Pascal si basa su questi valori atomici.
5.1.1 Tipo enumerativo
Per definire un tipo enumerativo basta elencarne (enumerarne) tutti i possibili valori, ovvero basta elencare tutti gli identificatori con cui sono denotati i valori del tipo, che, in pratica, rappresentano le costanti letterali che caratterizzano il tipo stesso.
Una variabile dichiarata di quel tipo potrà assumere solo tali valori.
Seguono alcuni esempi di tipi enumerativi:
type
colore = (rosso,arancio,giallo,verde,azzurro,indaco,violetto);
frutta = (mela,pesca,arancio,limone);
var
moneta: (dollaro,marco,sterlina,franco,yen,lira);
giorno: (lun,mar,mer,gio,ven,sab,dom);
portata:(antipasto,primo, secondo, contorno, dolce, frutta);
Nota:
Un identificatore di un valore scalare definito dall’utente può comparire nella definizione di un solo type.
L’ordine con cui compaiono nella enumerazione gli identificatori definisce l’ordinamento dei valori denotati da quei nomi.
Ciò permette di
• confrontare oggetti dei tipi enumerativi con operatori relazionali
lun < mer
rosso < violetto
• applicare le funzioni pred e succ che individuano il predecessore e il successore di un valore nella sequenza data nella definizione del tipo enumerativo
pred(mer) = mar
succ(gio)= ven
pred(lun) = undefined !
• ottenere la posizione del valore considerando numerato con zero il primo posto tramite la funzione ord
ord(lun)=0
ord(gio)=3
5.1.2 Tipo subrange
Valori: sottoinsieme dei valori di un ORDINAL TYPE (integer, boolean, char, enumerativi).
E’ definito indicando limiti inferiore e superiore.
Deve valere la seguente relazione
limite inferiore mer.
C’è validazione automatica nelle istruzioni, cioè i risultati devono appartenere al tipo scalare associato.
5.1.3 Tipi predefiniti
il Pascal supporta 4 tipi predefiniti denotati da altrettanti identificatori standard i cui valori sono insiti nel linguaggio stesso e non possono essere ridefiniti dall’utente.
a) tipo integer
Rappresenta l’insieme dei numeri interi rappresentabili nella word di memoria della macchina.
Quindi possiamo definire questo tipo standard come:
type
integer = -maxint-1..maxint
dove maxint
è un identificatore standard che denota il più grande numero intero rappresentabile sulla
macchina che dipende dalla lunghezza (numero di bit) della word e dalla notazione adottata.
Ogni operazione che porta fuori dal range -maxint-1..maxint produce un OVERFLOW error.
Tutti i tipi di dati che hanno una natura enumerabile possono essere rappresentati come integer.
const
dozzina = 12;
type
lunghezza = integer;
ora = 0..24;
var
perimetro : integer;
contatore: integer;
Questo tipo è caratterizzato da operazioni specifiche:
+ somma (operatore diadico) e segno (operatore monadico)
- sottrazione(oper. diadico) e segno(operatore monadico)
* moltiplicazione
^ esponenziazione (non ISO)
div divisione intera
mod modulo (resto della divisione intera)
abs valore assoluto
pred predecessore
succ successore
con risultato integer;
c divisione
arctan arcotangente
cos coseno
exp esponenziale
ln logaritmo naturale
sin seno
sqr quadrato
sqrt radice quadrata
con risultato reale;
odd dispari
= < > =
con risultato booleano.
b) tipo real
Rappresenta l’insieme dei numeri reali rappresentabili nella word di memoria della macchina.
In questo caso la limitazione fisica della lunghezza della parola di memoria della macchina introduce limiti
- sul range dei valori permessi
- sulla precisione degli stessi, con la conseguenza di rendere discreta la natura continua dei numeri reali
Rappresentazione Floating Point normalizzata, PRECISIONE MACCHINA e NUMERI MACCHINA.
Errori di OVERFLOW ed errori di UNDERFLOW.
Alcuni esempi di definizione con il tipo real sono.
const
g = 9.8;
type
pressione = real;
var
ascissa,ordinata : real;
Non essendo di utilità pratica che il tipo real sia riconosciuto come enumerativo, non si possono definire subrange reali.
Questo tipo è caratterizzato dalle seguenti operazioni specifiche:
trunc troncamento
round arrotondamento
con risultato integer;
+ somma (operatore diadico) e segno (operatore monadico)
- sottrazione(oper. diadico) e segno(operatore monadico)
* moltiplicazione
^ esponenziazione (non ISO)
abs valore assoluto
a divisione
arctan arcotangente
cos coseno
exp esponenziale
ln logaritmo naturale
sin seno
sqr quadrato
sqrt radice quadrata
con risultato reale;
< > = =
con risultato booleano.
c) Tipo boolean
La definizione del tipo boolean è la seguente:
type
boolean = (false,true);
I due valori di verità false e true sono identificatori standard.
Per essi vale la convenzione di ordinamento per cui
false >= = b espr. booleana “a contiene b”
a m) or (x ii=v); (* è finita la tabella *)
if x ii=v then (* o si è trovato il valore*)
found:=true
end;
begin
readln(k); (*lettura range tabella*)
for i:=1 to k do (*lettura tabella*)
readln(tab1 ii);
readln(key); (*lettura valore da cercare nella tabella*)
found:=false;
i:=0;
look(tab1,key,k);
if found then
write(‘trovato’)
else
write(‘non trovato)
end.
I parametri formali x,v ed m della procedura look sono sostituiti nella chiamata dagli attuali tab1,key e k.Il meccanismo di passaggio di parametro non è altro che una ricopiatura del valore del parametro attuale nella zona dello stack della procedura riservata al parametro formale corrispondente.
Ciò comporta
• uno spreco di memoria, e di tempo necessario per il trasferimento,
• l’impossibilità che qualsiasi cambiamento effettuato sul parametro passato per valore venga registrato all’esterno della procedura stessa essendo effettuato sulla copia locale della variabile.
Il passaggio dei parametri per valore consente anche parametri attuali definiti tramite espressioni aritmetiche, es. look(tab1,17,k-5). In questo caso il richiamo comporterà il calcolo del valore dell’espressione prima di effettuare il trasferimento del valore.
Nota I parametri attuali devono essere forniti con rispetto di:
• numero,
• ordine
• tipo
rispetto ai parametri formali.
Dunque i parametri sono posizionali, ovvero al primo parametro formale viene fatto corrispondere il primo parametro attuale, e così via per i successivi.
2. Passaggio per indirizzo
Il meccanismo di passaggio di parametro per indirizzo fornisce al sottoprogramma chiamato esclusivamente l’indirizzo del parametro attuale, cosicchè questo possa essere direttamente raggiunto e operato in lettura e scrittura dalla procedura chiamata.
Considerando il programma precedente possiamo riscrivere la procedura look con una nuova intestazione, specificando un nuovo parametro passato per indirizzo (trovato):
procedure look(x:table;v:integer;m:range; var trovato:boolean);
begin
repeat
i:=i+1
until (i>m) or (x ii=v);
if x ii=v then
trovato:=true
end;
dove il parametro formale trovato è per indirizzo.
Nel programma principale nel richiamo della procedura verrà passata la variabile found come parametro attuale per trovato.
look(tab1,key,k,found);
Ciò comporta che
• L’occupazione della memoria legata al parametro formale che viene allocata nello stack locale alla procedura è esclusivamente quella necessaria per contenere l’indirizzo del parametro attuale.
• Cambiando il valore di trovato nella procedura automaticamente viene alterato il valore di found nello stack del programma principale.
3. parametri procedure/funzioni
In Pascal è possibile passare procedure e funzioni come parametri.
In questo caso il parametro formale corrisponde al modello di una procedura o funzione il cui nome è fittizio, ma di cui vengono specificati i comportamenti (parametri...) e il parametro attuale sarà il nome questa volta reale della procedura/funzione passata.
10.3 Le funzioni
Una funzione è un sottoprogramma caratterizzato dal fatto che:
• è dotata di nome a cui viene associato un valore di ritorno
• è dotata di tipo, che viene dichiarato nella testata della funzione
• accetta informazioni in input (come parametri e non)
• fornisce risultati in output (come parametri e non, oltre al risultato relativo alla funzione stessa).
• nel corpo della funzione il suo nome deve apparire come parte sinistra di un assegnamento, che permette di attribuire ad esso il valore che verrà poi trasmesso al programma chiamante.
• il richiamo di una funzione avviene con l’inserimento del suo nome in un espressione nel corpo del modulo chiamante
Nota L’uso dei parametri nelle funzioni è assolutamente il medesimo che nelle procedure.
Per esempio consideriamo un programma che genera una successione di n numeri che sembrano distribuiti casualmente in modo uniforme nell’intervallo Pa,ba.
program Casuale(input,output);
var
r:real; (* variabile per i numeri da generare*)
i:integer; (* contatore*)
n:integer;
a,b:integer; (* estremi dell’intervallo *)
function rndm(x:intero):real;
(* dato un intero genera un valore reale *)
(* pseudocasuale compreso tra 0 e 1 escluso *)
begin
x:=(25173*(x-1)+13849)mod65536;
rndm:=x/65536
end;
begin
readln(n);
readln(a);
readln(b);
for i:=1 to n do
begin
r:=rndm(i)*(b-a)+a;
writeln(r)
end
end.
Dalle osservazioni fatte emerge che il nome di una funzione:
- a destra di un assegnamento ne rappresenta il valore,
- a sinistra invece ha il significato di locazione di memoria
del tutto compatibilmente con il concetto di variabile.
10.3 La ricorsione
Tra le risorse che una procedura o funzione può vedere c’è anche sé stessa.
Ciò permette ad una procedura o ad una funzione di richiamarsi, meccanismo che si definisce
Esiste una ricca classe di problemi che si presta ad una formulazione ricorsiva.
Esempio la funzione fattoriale definita come
0! = 1
n!=1*2*...*(n-1) *n
oppure ricorsivamente come
0! = 1
n!=(n-1)!*n
Utilizzando quindi la forma ricorsiva possiamo scrivere
program fattoriale(input,output);
var n:integer;
function fact(x:integer):integer;
(* calcola il fattoriale di x *)
begin
if x=0 then
fact:=1
else
fact:=x*fact(x-1)
end;
begin
readln(n);
writeln(fact(n))
end.
Si noti che ora il nome della funzione compare a destra di un’assegnament ed è ugualmente corretto.
Osserviamo cosa accade con la ricorsione visualizzando graficamente cosa accade nello stack delle variabili.
Supponiamo che in fase di esecuzione venga letto il valore n=2.
n = 2
Alla prima chiamata sarà allocata una variabile locale, corrispondente al nome fact e verrà copiato il parametro in x.
n = 2
fact
x = 2
siccome x è > 0 viene eseguita la clausola else della struttura if e quindi fact chiama ricorsivamente sé stessa, alla seconda chiamata lo stack sarà divenuto:
n = 2
fact
x = 2
fact
x = 1
siccome x èancora > 0 viene eseguita la clausola else della struttura if e quindi fact chiama di nuovo ricorsivamente sé stessa, alla terza chiamata lo stack sarà divenuto:
n = 2
fact
x = 2
fact
x = 1
fact 1
x = 0
Poichè ora x vale 0 l’attivazione attuale di fact, viene eseguita la clausola else della struttura if e quindi fact ritornerà con valore 1, da cui, eliminando lo spazio di memoria creato dall’ultima attivazione lo stack è:
n = 2
fact
x = 2
fact 1*1
x = 1
Proseguendo, all’uscita della seconda chiamata ricorsiva avremo
n = 2
fact 2*1
x = 2
Infine, all’uscita della prima attivazione della funzione avremo di nuovo:
n = 2
La ricorsione presentata in quest’esempio è detta ricorsione diretta, in quanto il richiamo ricorsivo avviene della funzione (o della procedura) avviene “direttamente” nel corpo della stessa.
Esistono altre situazioni in cui si parla di ricorsione mutua, in cui la procedura A chiama la procedura B che a sua volta richiama la procedura A.
11 Puntatori
Come abbiamo visto il Pascal alloca durante l’esecuzione le proprie variabili in modo dinamico, avvalendosi di un’area di memoria opportunamente dedicata, detta stack.
Tali variabili, create e distrutte man mano che si eseguono procedure e funzioni, vengono però dichiarate in modo statico dal programmatore che infatti non può determinarne istanze al di fuori “dell’ambiente dichiarativo”.
Tuttavia esistono le cosiddette variabili dinamiche che sono create e distrutte dinamicamnete durante l’esecuzione del programma completamente sotto il controllo del programmatore.
Per gestire variabili così strutturate uil Pascal mette a disposizione:
• il tipo pointer
• le procedure predefinite new e dispose.
Una variabile di tipo “puntatore a tipo” significa che tale variabile contiene l’indirizzo di una seconda variabile del tipo specificato.
Dunque la dichiarazione
type
ptr = ^integer;
var
x:ptr;
y, z:integer;
permette di attribuire:
• ad x il valore del puntatore ossia dell’indirizzo della variabile puntata
• a x^ il valore della variabile puntata.
Perciò dopo aver effettuato i seguenti assegnamenti
y:=3;
x:=^y;
z:=x^-4;
la situazione dello stack sarà:
x
y 7
z 3
In realtà la memoria non è proprio distribuita così.
In particolare memoria gestita dinamicamente dal programmatore detta heap risulta staccata dallo stack che contiene solo le variabili allocate durante la “lettura della fase dichiarativa”.
Onde evitare la presenza di troppe aree di memoria differenti per la gestione delle variabili, STACK e HEAP, che potrebbe portare a situazioni spiacevoli quali:
• problemi di sovrapposizione,
• problemi di dimensionamento (vedi casi in cui lo heap è quasi vuoto e lo stack è pieno oviceversa).
il Pascal usa una stessa area di memoria per ambedue, facendo crescere le aree in maniera contrapposta:
STACK
HEAP
In questo modo lo stack cresce verso il basso e lo heap verso l’alto e chi necessita maggiormente di memoria occupa quella disponibile.
La memoria si esaurisce quando le due aree collidono, e ciò solitamente comporta un messaggio di errore durante l’esecuzione di tipo “stack overflow”.
Il Pascal mette a disposizione una procedura predefinita:
new(x)
che applicata ad una variabile x di tipo pointer ha i seguenti effetti:
1. alloca nell’ apposita zona di memoria heap, un’area adeguata a contenere una variabile del tipo puntato dall’argomento;
2. inserisce nella variabile puntatore l’indirizzo di tale area di memoria.
Simmetricamente alla procedura new il pascal mette a disposizione la procedura
dispose(x)
che applicata ad una variabile x di tipo pointer:
restituisce lo spazio allocato dall’argomentorendendolo nuovamente disponibile per altre allocazioni.
Nota In realtà molti compilatori considerano in ogni caso tale spazio inusabile, altri ricompattano periodicamente,... il tutto a causa del fatto che non sempre si riesce a riutilizzare correttamente la memoria lascata disponibile.
A livello intuitivo possiamo pensare che un’intero occupi meno spazio di un array di 10 elementi, il chè porta ad avere un resto che può a sua volta essere riutilizzato, ma quasi mai del tutto, con conseguenti “buchi inutrilizzati”.
Infine il Pascal mette a disposizione la costante
nil (parola chiave)
Tale costante viene utilizzata per attribuire ad una variabile puntatore ads un qualsiasi tipo
un valore che indica che non sta puntando a nulla.

11.1 L’uso dei puntatori, un esempio: le liste
Def. LISTA
Sequenza di elementi di tipo definito in cui è possibile aggiungere e togliere elementi
==> struttura dati a dimensione variabile
in cui si accede al primo elemento della lista o a un sottinsieme ristretto di elementi
Un elemento può essere aggiunto o tolto specificandone la “posizione” all’interno della lista e raggiungendolo scandendo sequenzialmente la sequenza degli elementi.
Def. LUNGHEZZA DI UNA LISTA
il numero n degli elementi di L
La lista vuota si indica con s.
REALIZZAZIONE DI UNA LISTA CON PUNTATORI
Si utilizzano delle celle t.c. l’i-esima cella contiene: l’i-esimo elemento della lista
e il puntatore (indirizzo) all’elemento successivo
Lista vuota (lista di lunghezza 0)
L
Lista con un elemento (lista di lunghezza 1)
L
Lista con n elementi (lista di lunghezza n)
L ..... .....
N.B. L è il puntatore alla testa della lista
Utilizzando i puntatori del Pascal otteniamo la seguente dichiarazione di tipo:
type
cella = record
elem: tipoelem;
next: ^cella;
end
lista = ^cella;
posizione = ^cella;
.....

p p^.next p^.next^.next

p^.elem p^.next^.elem
12 File
Il concetto di file è legato all’idea di poter avere informazioni in grandi quantità su supporti esterni.
Non si può pertanto pensare di allocare una variabile di tale tipo sulla memoria centrale (nello stack).
In particolare la dichiarazione di una variabile di tipo file corrisponde all’allocazione in memoria di:
• un’area, detta buffer, organizzzata come uno degli elementi del file,
• una variabile puntatore, che punta a tale area, corrispondente al nome del file.
Quindi volendo definire:
type
libro = record
autore,titolo,editore:array[1..30]of char;
prezzo:integer
end;
biblioteca: file of libro;
var
x:biblioteca;
il modo per accedere ad un elemento della variabile x di tipo biblioteca è
x^
che mette a disposizione il valore corrente del buffer.
Vediamo ora come mettere in relazione il buffer con il file vero e proprio.
12.1 Input files
Il Pascal mette a disposizione le seguenti procedure:
• reset(filename) che predispone il file alla lettura partendo dal primo elemento e
se il file è vuoto attribuisce il valore true alla funz. eof e x^ risulta indefinito
altrimenti inserisce nel buffer(x^) il primo elemento del file.
• get(filename) che sposta il buffer sul prossimo elemento disponibile sul file;
se il file è terminato attribuisce il valore true alla funzione eof.
e la seguente funzione:
• eof(filename) che ritorna true se durante le operazioni sul file è stata raggiunta la sua fine,
false altrimenti.
Essendo il file una variabile esterna al programma, deve essere ad esso passata tramite specificazione nella lista che segue il nome del programma stesso.
Consideriamo ad esempio un programma che suppone di avere a disposizione un file x di interi da leggere e totalizzare:
program somma (input,output,x);
var x:file of integer;
s: integer;
begin
reset(x);
s:=0;
while not eof(x) do
begin
s:=s+x^;
get(x)(*nuovo elemento inserito nel buffer
oppure eof*)
end;
writeln(‘somma:’,s)
end.
Altre procedure più sofisticate del get per operare su files in input, è la procedura read con i seguenti funzionamenti:
• read(filename,a) che equivale a a:=filename^;
get(filename);
• read(a) che equivale a read(input,a);
• read(filename,a,b,c,..)che equivale a read(filename,a);
read(filename,b);
read(filename,c);
L’esempio precedente potrebbe quindi essere riscritto:
program somma (input,output,x);
var x:file of integer;
s,i: integer;
begin
reset(x);
s:=0;
while not eof(x) do
begin
read(x,i);
s:=s+i
end;
writeln(‘somma:’,s)
end.
12.2 Gli output files
Il Pascal mette a disposizione in modo simmetrico rispetto all’input, le procedure:
• rewrite(filename) che predispone il file in scrittura,
distrugge eventuali dati presenti sul file,
e setta eof a true.
• put(filename) che scrive fisicamente sul file il contenuto del buffer nella prima posizione
disponibile.
Consideriamo come esempio un programma per scrivere un certo insieme di interi su un file.
program writeint(input,output,x);
var x:file of integer;
i:integer;
begin
rewrite(x);
for I:=1 to 100 do
begin
x^:=i;
put(x)
end
end.
Sempre simmetricamente a quanto avviene per il caso di file in input, le operazioni di output possono essere facilitate dalla procedura write con i seguenti funzionamenti:
• write(filename,a) che equivale a filename^:=a;
put(filename)
• write(a) che equivale a write(output,a)
• write(filename,a,b,c,...) che equivale a write(filename,a);
write(filename,b);
write(filename,c);
12.3 I file di testo o textfiles
I textfiles sono dei file di caratteri, dotati perciò di tipo predefinito
Perciò sono equivalenti le scritture:
var
x:text;
var
x:file of char;
Su tali tipi di file si possono utilizzare procedure o funzioni più ampio dei normali files e con un significato leggermente diverso.
Supponiamo di voler leggere un intero da tastiera:
la digitazione dei tasti comporta l’invio al calcolatore della corrispondentestringa di caratteri
tale stringa deve essere tradotta nel corrispondente valore intero in rappresentazione binaria.
Analogamente la stampa di un intero su video presuppone
la conversione del valore intero dalla rappresentazione binaria alla stringa di caratteri
quindi l’operazione di trasposizione su video vera e propria.
Tali operazioni vengono fatte automaticamentedalle procedure read e write nell’ambito dei textfiles.
Un’altra caratteristica interessante dei textfiles è la possibilità di considerare il carattere di fine-linea, che significa che i textfiles riconoscono la struttura a linee.
Nota:
Si può osservare che lo standard input e lo standard output sono a tutti gli effetti dei textfiles.
Vediamo meglio quali sono le primitive con cui è possibile operare sui textfiles.
• read(filename,a,b,c,...) il comportamento è lo stesso che nei file normali,
a parte il fatto che qualora a, b, c siano variabili integer o real
le stringhe di caratteri in ingresso vengono automaticamente
convertite nella rappresentazione interna dei valori relativi.
• readln(fillename,a,b,...) funziona come la precedente, a parte il fatto che alla fine dell’operazione di lettura si posiziona sulla riga successiva
ignorando eventuali altri caratteri sulla riga precedente.
• write(filename,a,b,c,...) il comportamento è lo stesso che nei file normali, a parte il
fatto che qualora a, b, c siano variabili integer o real essi
vengono automaticamente convertiti nella forma di caratteri.
• writeln(filename,a,b,...) funziona come la precedente, a parte il fatto che alla fine
dell’operazione di visualizzazione si posiziona sulla riga successiva.
• eof(filename)funziona come per i file normali.
• eoln(filename)è una funzione booleanache assume il valore true
se tutti i caratteri di una linea sono stati letti (nel caso di input)
o se tutti gli spazi di una linea sono stati scritti (nel caso di output)
1

Esempio